Home›Certora›API
Certora · API

Certora API, as shipped in the download

Certora REST API, MCP & Webhooks (v3.2)

Certora ships a JSON REST API and signed webhooks so your LMS, course platform or HR system can issue certificates programmatically — and so anyone can verify a certificate by code without an account.

The batch endpoint is the point. A cohort finishes a course, your LMS makes one call with the whole roster, and every learner gets a certificate with its own verification code.

Authentication

Create a key in Settings → API & Webhooks. Send it on every call:

Authorization: Bearer apk_...

The key is shown once at creation and stored as a SHA-256 hash. Revoke any time.

One endpoint is deliberately public: GET /api/verify/{code} needs no key. A verification code that only its issuer can check would be pointless — employers and registrars must be able to verify. That endpoint reuses the public verify page's per-IP throttle (20 lookups / 60 s), so it cannot be used to guess codes.

Endpoints

Method & pathWhat it does
GET /api/pingVerify a key
GET /api/templatesCertificate templates with their tokens and validity period
GET /api/certificatesNewest 200 issued certificates; ?recipient= filters by name (substring)
POST /api/certificatesIssue one certificate or a whole batch
GET /api/verify/{code}PUBLIC — verify a code, no key required
GET /api/openapi.jsonOpenAPI 3.0 description of everything above

Issue one

curl -X POST https://certs.example.com/api/certificates \
  -H "Authorization: Bearer apk_..." -H "Content-Type: application/json" \
  -d '{"template_id":1,"recipient":{"name":"Ada Lovelace","course":"Fire Safety"}}'

Issue a batch (the LMS case)

curl -X POST https://certs.example.com/api/certificates \
  -H "Authorization: Bearer apk_..." -H "Content-Type: application/json" \
  -d '{"template_id":1,"issue_date":"2026-06-30","recipients":[
        {"name":"Ada Lovelace","course":"Fire Safety"},
        {"name":"Grace Hopper","course":"Fire Safety"},
        {"name":"Alan Turing","course":"Fire Safety"}]}'

Returns 201 with every issued certificate, each carrying its own code, verify_url and pdf_url. A batch is capped at 2000 recipients per call.

Validation is all-or-nothing. Every recipient is checked before anything is written, so a bad row never leaves you with half a cohort certified and a 422 you cannot interpret.

Recipients are accepted inline — you do not have to create recipient records first. Your LMS already owns the roster.

Verify (public)

curl https://certs.example.com/api/verify/ABCD-EFGH-JKMN
{ "found": true, "valid": true, "status": "valid",
  "code": "ABCDEFGHJKMN", "display_code": "ABCD-EFGH-JKMN",
  "recipient_name": "Ada Lovelace", "course": "Fire Safety",
  "issuer": "Ridgeline Training", "issue_date": "2026-06-30", "expires_at": "2029-06-30" }

status is one of valid, expired, revoked — computed by the same engine the web verify page uses. Unknown codes return 404 with {"found": false}. Too many lookups return 429.

Since 3.2 a lookup here counts on the certificate exactly like one on the web page (the MCP verify_certificate tool, your own agent, does not count). Every certificate object the API returns carries verify_count, last_verified_at (UTC, or null) and linkedin_clicks.

Public pages and badges (3.2)

No key and no sign-in; all on your own domain, built from Settings → Public verify URL.

Method & pathWhat it does
GET /verify/{code}/linkedinCounts an "Add to LinkedIn" press, then redirects to LinkedIn's Add-to-profile form filled in with the certificate (valid certificates only; otherwise 404)
GET /badges/issuer.jsonOpen Badges 2.0 issuer profile (name, verify page, contact email)
GET /badges/templates/{id}.jsonOne badge class per template
GET /badges/templates/{id}.svgThe badge image
GET /badges/{code}.jsonOne assertion per certificate: hosted verification, the recipient's email hashed (sha256$… with its salt), issuedOn, expires. A revoked certificate answers 410 with "revoked": true
GET /my · POST /myA person asks for a link to their certificates; it is emailed through your SMTP only when that address holds certificates, and the page answers the same either way
GET /my/{token}Every certificate issued to that email address; the link works for 30 days

Badges answer 404 until they are switched on in Settings with a contact email, and a certificate issued without a recipient email has no badge.

MCP (Model Context Protocol) — new in 3.0

Certora speaks MCP over stateless streamable HTTP at POST /mcp. Authentication is the same revocable API key as the REST API (Authorization: Bearer <key>), and a key carries its owner's role, so an agent can never do more than the person whose key it is holding.

GET /mcp answers 405 with Allow: POST — deliberately without the key check, so a probing client is told "right endpoint, wrong verb" instead of a bare 401 that reads as "no MCP here".

POST /mcp   {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}
POST /mcp   {"jsonrpc":"2.0","id":2,"method":"tools/list"}
POST /mcp   {"jsonrpc":"2.0","id":3,"method":"tools/call",
             "params":{"name":"issue_certificates",
                       "arguments":{"template_id":1,"recipients":[4,5,6]}}}
ToolWrites?What it does
list_templatesnoTemplates with orientation, validity period and how many each has issued
template_detailnoOne template, its merge tokens, and the certificates issued from it
list_certificatesnoThe register; filter by template_id, status, search
issue_certificatesyesThe batch operation — one certificate per recipient, each with its own code and a snapshotted expiry
verify_certificatenoThe same lookup the public /verify page performs
expiring_reportnoExpiring within N days, plus those already expired

Argument hygiene. Tool arguments take the same scalar guard as the REST body: a value may not be an array or object unless the tool allowlists it. issue_certificates allowlists exactly one — recipients — because that is the list it exists to take. Unknown arguments are refused by name, and enum values are refused with the list of what is accepted.

One path, one answer. issue_certificates calls the same writer the Generate screen calls, so the merge, the code generation and the expiry snapshot cannot drift between surfaces. A certificate issued by an agent verifies identically through verify_certificate, GET /api/verify/{code} and the public page. verify_certificate returns the same shape this REST API already documents.

Connect it to an assistant

Mint the key in the app first: Settings → API keys. Choose Read only when the assistant should answer questions but never change anything — the endpoint then lists only the read tools and refuses the rest by name, so a careless prompt cannot write. Full access behaves as before.

Every client needs the same three facts, and nothing in the handshake is vendor-specific:

The addresshttps://certs.example.com/mcp
The keyheader Authorization: Bearer apk_xxxx
The transportMCP over streamable HTTP, stateless

Claude — one command, or the same URL and header as a custom connector in the desktop and web apps:

claude mcp add --transport http certora https://certs.example.com/mcp \
  --header "Authorization: Bearer apk_xxxx"

ChatGPT and the OpenAI API — one entry in the Responses API's tools array (in ChatGPT itself, the same URL and key go in as a connector):

{
  "type": "mcp",
  "server_label": "certora",
  "server_url": "https://certs.example.com/mcp",
  "authorization": "apk_xxxx",
  "require_approval": "never"
}

**Every other client spells the same three facts differently — copy the shape from its own documentation, not from here.** VS Code is the clearest example of why: its configuration reference (read 6 September 2026) puts servers in .vscode/mcp.json under a "servers" object — *"an object that maps server names to their configurations"*. Pasted as-is, a block written for another client will not load there. The id, the URL and the token are what travel; the JSON around them belongs to whichever client you are configuring.

A local model, n8n, or your own code — n8n's MCP Client node takes the URL and the same Authorization: Bearer header; a model running on your own machine reaches it through any MCP client, so nothing leaves your network at all. Writing it yourself is one POST of JSON-RPC 2.0:

curl -X POST https://certs.example.com/mcp \
  -H "Authorization: Bearer apk_xxxx" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Any client that speaks MCP's streamable-HTTP transport works, including ones that do not exist yet: Certora implements the protocol, not an integration with a particular vendor.

Webhooks

Add a receiver in Settings → API & Webhooks and choose events:

EventFires when
certificate.issuedA certificate is issued — from Generate, the REST API or /mcp
certificate.revokedA certificate is revoked — one at a time or in bulk from the register (once per certificate; revoking one already revoked sends nothing)

A batch of N recipients fires N separate certificate.issued events — one per certificate, each with its own code. That keeps a single event shape whether you issued one or a thousand.

Every delivery is signed:

X-Certora-Event: certificate.issued
X-Certora-Signature: sha256=<hmac_sha256(raw_body, your_webhook_secret)>

Compare with hash_hmac('sha256', $rawBody, $secret) and reject on mismatch. Deliveries and their response codes are logged in the same settings page.

Errors

CodeMeaning
401Missing, unknown or revoked key
404No such certificate / code
422Validation — the detail field names the offending field
429Verification lookups throttled (public endpoint only)

Connect it to Zapier, Make or n8n

Certora talks to other tools without a plugin: it sends a webhook (an HTTPS message) the moment something happens, and it accepts calls to the API above. Zapier, Make and n8n can use both.

Before you start

  • In Certora, open Settings → API & Webhooks. Mint an API key (choose Read only if the other tool only reads), and add a webhook when you have its URL (below).
  • Use your install's full address, for example https://certora.yourcompany.com.

When something happens in Certora: send it to another tool

  1. In the other tool, create the trigger that gives you a URL to receive webhooks: - Zapier: the "Webhooks by Zapier" app, trigger Catch Hook (Webhooks by Zapier needs a paid Zapier plan). - Make: module Webhooks → Custom webhook. - n8n: the Webhook node.
  2. Copy that URL into Certora under Settings → API & Webhooks, and choose the event or events below.
  3. Make the event happen once in Certora, so the other tool receives a sample and you can map its fields.
EventFires when
certificate.issuedA certificate is issued — from Generate, the REST API or /mcp
certificate.revokedA certificate is revoked — one at a time or in bulk from the register (once per certificate; revoking one already revoked sends nothing)

Every delivery carries X-Certora-Signature, an HMAC-SHA256 signature over the raw body. Zapier and Make accept the delivery without checking it, so keep the webhook URL private. In n8n you can check it in a Code node before using the data.

When something happens elsewhere: act in Certora

  • Zapier: "Webhooks by Zapier", action POST (or Custom Request).
  • Make: module HTTP → Make a request.
  • n8n: the HTTP Request node.

Send the request to your install's address plus the path from the Endpoints table above, with the header Authorization: Bearer <your API key> and a JSON body. A read-only key cannot write.

The endpoints that change something:

  • POST /api/certificates: Issue one certificate or a whole batch

Example recipes

  • certificate.issued → a row in Google Sheets: webhook trigger on certificate.issued (A certificate is issued — from Generate, the REST API or /mcp), then the Google Sheets "Create row" action with the fields you want.
  • certificate.revoked → a message in Slack or Teams: webhook trigger on certificate.revoked (A certificate is revoked — one at a time or in bulk from the register (once per certificate; revoking one already revoked sends nothing)), then the Slack or Microsoft Teams "Send message" action.
  • A new form entry or row elsewhere → Certora: the other tool's trigger (for example a new Google Sheets row or a form submission), then an HTTP action calling POST /api/certificates (Issue one certificate or a whole batch).

These recipes use only the events and endpoints documented above. We have not run them inside Zapier, Make or n8n ourselves; Certora's own automated tests check its API.

← Back to Certora · Manual · Quickstart · Test run