← Back to the Citation Checker

Use the Citation Checker in EduGenAI

Register this tool as an EduGenAI Extension so the assistant can verify a paper's references against OpenAlex β€” inside your own EduGenAI chat, with no separate LLM key.

⬇ Download PDF version Open the standalone tool

How it works

An EduGenAI Extension is a function-calling tool: you give EduGenAI an HTTP endpoint and a function definition, and its assistant calls that endpoint (as a JSON request) whenever it needs to. This citation checker splits cleanly across the two sides:

StepWho does it
Read the paper, pull out the reference list and the sentences that cite each sourceEduGenAI's assistant β€” the paper is already in its chat context
Look each reference up in OpenAlex; check title, authors, year, journal, DOI, volume, issue, pages; return the abstractThis extension endpoint β€” deterministic, no LLM
Compare the citing sentence to the abstract and flag misquotesEduGenAI's assistant, using the abstract the endpoint returned
Because the reasoning stays on EduGenAI's side, the extension needs no LLM API key. It only wraps OpenAlex, which is free. (An optional OpenAlex Premium key can be stored securely in the Headers / Azure Key Vault section β€” see Step 3.)

Before you start

You need this app deployed at a public HTTPS URL β€” EduGenAI calls it server-to-server. Everywhere below, replace https://YOUR-DEPLOYMENT-HOST with your deployment's address.

Step by step

  1. Open the Extension builder

    In EduGenAI, create a new Extension (the panel with Name, Short description, Headers, and Functions).

  2. Name and describe it

    Name

    OpenAlex Citation Verifier

    Short description

    Verifies a paper's references against OpenAlex: checks that each work exists and that the printed title, authors, year, journal, DOI and pages match, and returns the abstract.

    Detail description β€” this is the instruction EduGenAI's assistant follows. Paste it verbatim:

    Use this extension to fact-check the reference list of an uploaded paper.
    
    STEP 1 - EXTRACT. Read the document. For every entry in the reference list, pull out: title, the full list of author names in order, whether it ends in "et al.", year, DOI (only if printed), journal/venue, volume, issue, and page range. Also find the sentence(s) in the body where each source is cited, with one sentence of context on each side. Keep those citation sentences in your own working notes for Step 3 - do NOT send them to the function.
    
    STEP 2 - VERIFY. Call the "verify_references" function once, passing every reference. For each one it returns: "badge" and "severity" (0-100), "status" (found / fuzzy / not_found / lookup_failed), "mismatched_fields", a "field_check" comparing each printed detail to OpenAlex (with reference_value vs openalex_value), the matched "work" (authors, year, venue, doi, url) and its "abstract", and - for FUZZY matches only, where "work" is null and "field_check" is empty - a "candidates" array of the closest works (each with title, authors, year, venue, doi, url).
    
    STEP 3 - JUDGE MISQUOTES. For each reference whose status is "found", compare the citation sentence(s) you kept in Step 1 with the returned "abstract". If the student uses the source as though it were about a different topic than the abstract shows (e.g. the abstract is about macroeconomic productivity but the student cites it for labour productivity specifically), that is a MISQUOTE. Otherwise it is consistent. If there is no abstract, or the reference is never cited in the body, the misquote check is "unclear". Never invent a verdict.
    
    STEP 4 - PRESENT (follow this format exactly). The user must NEVER see the raw JSON from the function - it is a machine interface. Convert it into ONE readable Markdown table, one row per reference, sorted by DESCENDING severity. Use the function's "severity" value; if you judge a reference to be a misquote MISMATCH, raise its severity to AT LEAST 80 (keep the function's number if it is already higher). Columns:
    
    | # | Flag | Reference (as printed) | Status | Metadata issues | Misquote | What the paper is about |
    
    - Flag: πŸ”΅ if status is lookup_failed (it could not be checked); otherwise πŸ”΄ if severity >= 70, 🟠 if 45-69, 🟒 otherwise.
    - Status: the "badge" value with an emoji - πŸ”΄ Potential hallucination (not_found), 🟠 Fuzzy match (fuzzy), πŸ”΅ Lookup failed (found no result to compare), 🟒 Verified (found).
    - Metadata issues: if "mismatched_fields" is empty, write "-". Otherwise, for each mismatched field take its reference_value and openalex_value from "field_check" and write:  field: "printed" -> "OpenAlex".  Put **authors** in bold when authors is one of them. (For a fuzzy row there is no field_check yet - write "-".)
    - Misquote: πŸ”΄ Mismatch / 🟠 Likely mismatch / 🟒 Consistent / βž– Unclear, plus a 6-10 word reason. (Only "found" rows have a misquote; for others write "-".)
    - What the paper is about: one short clause summarising the "work" abstract, or "-" if none.
    
    After the table, add a "Details" section covering only the πŸ”΄ and 🟠 rows:
    - For a FOUND row: the matched title as a link to its "work.url", the field-by-field comparison, and one sentence on the misquote if any.
    - For a FUZZY row: "work" is null - instead list the entries in "candidates" (title as a link to each "url", with authors and year) and ask the user to confirm which, if any, is the intended source. Do NOT present a fuzzy row as verified.
    
    End with one summary line: "N references - X verified, Y fuzzy, Z potential hallucinations, plus any metadata/misquote flags."
    
    Never invent a DOI, author, year, or verdict the function did not return. If "status" is "lookup_failed", say the reference could not be checked - do NOT call it a hallucination.
  3. Add the Header

    In the Headers section, add one header so the endpoint receives JSON:

    KeyValue
    Content-Typeapplication/json

    Optional β€” OpenAlex Premium: if you have an OpenAlex Premium key and want higher rate limits, add a second header X-OpenAlex-Key and paste the key as its value. Use the Secure header values option so it is stored in Azure Key Vault, not in plain text.

  4. Add the function

    Under Functions, add a function that points at the batch endpoint.

    Method & URL

    POST  https://YOUR-DEPLOYMENT-HOST/api/verify_batch

    Function definition (name, description, JSON parameters)

    {
      "name": "verify_references",
      "description": "Verify a list of bibliographic references against OpenAlex. For each reference, returns whether the work exists (found / fuzzy / not_found), a field-by-field comparison of the printed metadata (title, authors, year, journal, DOI, volume, issue, pages) against the real record, and the abstract of the matched work.",
      "parameters": {
        "type": "object",
        "properties": {
          "references": {
            "type": "array",
            "description": "Every reference in the paper's bibliography.",
            "items": {
              "type": "object",
              "properties": {
                "title":   { "type": "string", "description": "The work's title." },
                "authors": { "type": "array", "items": { "type": "string" }, "description": "Every author name printed, in order, e.g. 'Smith, J.'." },
                "et_al":   { "type": "boolean", "description": "True if the reference abbreviates the author list with 'et al.'." },
                "year":    { "type": "integer" },
                "doi":     { "type": "string", "description": "Only if printed in the reference." },
                "journal": { "type": "string", "description": "Journal or venue name." },
                "volume":  { "type": "string" },
                "issue":   { "type": "string" },
                "pages":   { "type": "string", "description": "Page range as printed, e.g. '123-145'." }
              },
              "required": ["title"]
            }
          }
        },
        "required": ["references"]
      }
    }
  5. Submit and test

    Click Submit. Then, in an EduGenAI chat, upload a paper (or paste its reference list) and ask: β€œCheck the citations in this paper against OpenAlex.” The assistant will call your endpoint and return the verified table.

What the endpoint returns

Example request EduGenAI sends to /api/verify_batch:

{
  "references": [
    {
      "title": "Highly accurate protein structure prediction with AlphaFold",
      "authors": ["Smith, J.", "Jones, B."],
      "et_al": false,
      "year": 2019,
      "journal": "Science",
      "pages": "100-110"
    }
  ]
}

Example response (abbreviated) β€” note the wrong authors, year, and journal are caught, and the abstract is returned for the misquote step:

{
  "count": 1,
  "results": [
    {
      "index": 1,
      "status": "found",
      "badge": "Verified",
      "severity": 85,
      "priority": "Review",
      "mismatched_fields": ["authors", "year", "journal"],
      "work": {
        "title": "Highly accurate protein structure prediction with AlphaFold",
        "authors": ["John Jumper", "Richard Evans", "..."],
        "year": 2021,
        "venue": "Nature",
        "doi": "10.1038/s41586-021-03819-2",
        "url": "https://doi.org/10.1038/s41586-021-03819-2",
        "abstract": "Proteins are essential to life ... (returned for the misquote check)"
      },
      "field_mismatch_count": 3,
      "field_check": [
        { "field": "title",   "status": "match" },
        { "field": "authors", "status": "mismatch", "reference_value": "Smith, J., Jones, B.", "openalex_value": "John Jumper, Richard Evans, ...", "detail": "Not on the record: Smith, J., Jones, B." },
        { "field": "year",    "status": "mismatch", "reference_value": 2019, "openalex_value": 2021 },
        { "field": "journal", "status": "mismatch", "reference_value": "Science", "openalex_value": "Nature" }
      ],
      "notes": ["Metadata mismatch on: authors, year, journal. The work exists but the citation details differ."]
    }
  ]
}
A single-reference endpoint is also available at POST /api/verify β€” same fields, but without the outer references array β€” if you prefer one call per reference. The batch endpoint is recommended for a whole bibliography (one round trip, kinder to OpenAlex's rate limits).

From JSON to a readable table β€” what you'll see in chat

You never look at that JSON. It is the machine interface between EduGenAI and the endpoint. The Detail description you pasted in Step 2 tells EduGenAI's assistant to convert it (STEP 4) into a severity-sorted table with flags, scores, and the mismatches spelled out β€” mirroring how this site presents results. A typical chat answer looks like:

#FlagReference (as printed)StatusMetadata issuesMisquoteWhat the paper is about
3πŸ”΄Zorblatt, Q. (2021). Quantum blockchain effects on medieval cheese tradingβ€¦πŸ”΄ Potential hallucinationβ€”β€”β€”
2πŸ”΄Smith, J. & Jones, B. (2019). Highly accurate protein structure prediction… Science, 100-110.🟒 Verifiedauthors: "Smith, Jones" β†’ "Jumper, Evans…" Β· year: 2019 β†’ 2021 Β· journal: Science β†’ NatureπŸ”΄ Mismatch β€” cited for labour productivity; paper is protein-structure AIDeep-learning protein structure prediction
1🟒Vaswani, A. et al. (2017). Attention is all you need. NeurIPS.🟒 Verifiedβ€”πŸŸ’ ConsistentAttention-only sequence model (Transformer)

…followed by a Details section for the flagged rows and a one-line summary count. The assistant does this conversion because the Detail description explicitly says the user must never see the raw JSON.

If you still see raw JSON in the chat: the Detail description probably wasn't saved with the extension (re-check Step 2), or the platform truncated it. Two fixes: (1) re-paste the full Detail description; (2) as a per-chat override, end your request with β€” β€œPresent the results as a severity-sorted table with flags and the metadata mismatches spelled out; do not show raw JSON.” The endpoint's JSON also ships ready-made display fields (badge, severity, priority, mismatched_fields) precisely so the assistant only has to lay them out, not compute them.

What data passes through your server?

Because the extension calls your deployment, it helps to be precise about what actually travels there β€” and what does not.

DataWhere it goes
The paper's full text, the student's name, and the citing sentencesStay inside EduGenAI. The assistant keeps the citing sentences for its own misquote reasoning; they are never sent to your endpoint.
Reference metadata only β€” title, authors, year, DOI, journal, volume, issue, pagesSent to your /api/verify_batch endpoint, and (titles/DOIs) on to OpenAlex for the lookup.
Any LLM keyNot involved server-side at all β€” the reasoning runs on EduGenAI's model.
So, does β€œall data go through me”? No β€” only the bibliographic metadata of the references, and only in transit. The endpoint reads just those fields; the citing sentences aren't part of what the extension sends, and anything else that were included would be discarded unread (never logged or stored). Just the function call? Yes; that is the only thing that reaches your server. Is it logged? Not by this app β€” it is stateless (no database, no file writes) and never logs request bodies or keys. Whatever hosting stack or reverse proxy you deploy behind may keep its own access log (request line: method + path + status, not bodies) β€” that part is under your control, and you can turn it down.

One caveat about the OpenAlex Premium key: it is used per-request, never stored, and scrubbed from any error message this app returns. But when your endpoint calls OpenAlex, the key travels as a query parameter on the outbound URL β€” so if you run behind an egress proxy that logs full outbound URLs, it could appear there. If that matters, don't set a Premium key (the tool works fine without one) or use a proxy that redacts query strings.

Notes & limits