Skip to content

Certificate API (/certificates)

All routes are prefixed with /certificates.

Examples use BASE_URL="https://ebg-tokenizer.u3dev.deno.net" unless your operator provides another origin (HTTP API overview).

  • Read-only routes need a valid certificate contract :address. The deployment reads Arbitrum over RPC.
  • Write routes need a server-side signer. Clients do not sign chain transactions.

Discover collection addresses with the factory API: GET /factory/certificate-count and GET /factory/certificates/:index.

Quick reference

MethodPathSigner required
POST/certificates/:address/mintYes
PUT/certificates/:address/tokens/:tokenIdYes
PATCH/certificates/:address/tokens/:tokenIdYes
GET/certificates/:address/tokens/:tokenId/certificate
GET/certificates/:address/tokens/:tokenId/tokenURI
GET/certificates/:address/balance-of/:account
GET/certificates/:address/owner-of/:tokenId
GET/certificates/:address/approved/:tokenId
GET/certificates/:address/token-uri/:tokenId
GET/certificates/:address/total-supply
GET/certificates/:address/token-by-index/:index
GET/certificates/:address/token-of-owner-by-index/:owner/:index
GET/certificates/:address/name
GET/certificates/:address/symbol
GET/certificates/:address/owner
GET/certificates/:address/is-approved-for-all/:ownerParam/:operator
GET/certificates/:address/is-minter/:account
GET/certificates/:address/nonces/:tokenId

Certificate metadata fields

Used by POST …/mint, PUT …/tokens/:tokenId (full body), and PATCH …/tokens/:tokenId (subset). Keys are snake_case.

FieldType (request)Notes
registration_dateinteger or decimal string
hours_numberinteger or decimal string
sessions_numberinteger or decimal string
delivery_correlativestring
participant_namesstring
participant_last_namesstring
course_namestring
issuing_institutionstring
image_urlstring
certificate_urlstring

Responses that return certificate data use the same keys; registration_date, hours_number, and sessions_number are decimal strings.


NFT ERC-721 metadata (GET /metadata)

Wallets resolve tokenURI to JSON. This route is public (no x-api-key) and is not under /factory or /certificates.

Path: GET /metadata/:chainId/:addressOrCertificateId/:tokenId

ParamNotes
:chainIdMust be 42161 (Arbitrum One). Other values → 400.
:addressOrCertificateIdEither a checksummed collection address (0x…) or a decimal factory certificate id. If numeric, the service resolves the address via onchain certificates(id) on the factory (factory address must be configured). 404 if the resolved address is zero.
:tokenIdNon-negative integer as a decimal string in the path.

Behavior

  • Resolves the collection address, reads onchain certificate(tokenId), maps to ERC-721 Metadata JSON (name, description, image, external_url, attributes).
  • Response headers include Cache-Control: public, max-age=3600, s-maxage=86400.

Aligning with tokenURI

Set base_uri (≤ 80 characters onchain) so tokenURI(tokenId) equals base_uri + decimal tokenId (same format as the last path segment).

  • By address: https://api.example.com/metadata/42161/0xYourCertificate/ + 0
  • By certificate id (matches auto base_uri from POST /factory/certificates): https://api.example.com/metadata/42161/7/ + 0

The contract and this route must use the same concatenation (no .json suffix unless the contract adds it).


POST /certificates/:address/mint

Purpose

Mints one NFT for the collection at :address by calling the factory’s mint_certificate. The service waits for the receipt, scans logs for CertificateMinted, and returns structured mint and transaction data. The hot wallet must have MINTER_ROLE on the factory; the token is minted to the recipient in the JSON body, not necessarily to the signer.

Auth

When API key auth is enabled (overview):

  • x-api-key with master or minter; read keys get 403 on POST.
  • 401 — missing or invalid key.

Request

  • Headers: Content-Type: application/json
  • Path
ParamNotes
:addressCertificate contract address

Body (JSON)

FieldTypeRequiredNotes
tostringYesRecipient; aliases recipient, owner (first present wins). Must be a valid address.
certificateobjectYes*One of certificate, data, or certificateData (same shape). See Certificate metadata fields.
dataobjectYes*Alternative to certificate.
certificateDataobjectYes*Alternative to certificate.
certificateAddressstringNoIf set, must match :address.

Semantics

  • Single onchain transaction submitted by the service; response transaction.status reflects the receipt (e.g. success).
  • If the receipt has no matching CertificateMinted event for this certificate, the API returns 500 (see Errors).

Responses

200 OK

FieldTypeNotes
mint.tokenIdstringMinted token id
mint.certificateAddressstringSame as :address
mint.mintedTostringRequest recipient
transaction.hashstringTx hash
transaction.blockNumberstringBlock number
transaction.statusstringReceipt status
transaction.gasUsedstringGas used
json
{
  "mint": {
    "tokenId": "1",
    "certificateAddress": "0x…",
    "mintedTo": "0x…"
  },
  "transaction": {
    "hash": "0x…",
    "blockNumber": "123456789",
    "status": "success",
    "gasUsed": "210000"
  }
}

Errors

StatusSituation
400Invalid JSON, missing certificate tuple / recipient, address mismatch, invalid certificateAddress or to
401API key missing or invalid when auth is enabled
403Read-only API key
500Mint reverted, RPC/signer failure, or receipt ok but no matching CertificateMinted log: { "message": "Mint succeeded but CertificateMinted event was not found" }
502Upstream failures as surfaced by the service

Example

bash
curl -sS -X POST "$BASE_URL/certificates/0xYourCertificate/mint" \
  -H "Content-Type: application/json" \
  -H "x-api-key: $MINTER_KEY" \
  -d '{
    "to": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    "certificate": {
      "registration_date": 1710892800,
      "delivery_correlative": "2024-001",
      "participant_names": "Ada",
      "participant_last_names": "Lovelace",
      "course_name": "Intro",
      "hours_number": 40,
      "sessions_number": 10,
      "issuing_institution": "Example University",
      "image_url": "https://example.com/img.png",
      "certificate_url": "https://example.com/cert.pdf"
    }
  }'

PUT /certificates/:address/tokens/:tokenId

Purpose

Full replacement of onchain certificate metadata for tokenId via one update_certificate call. The JSON body is the certificate struct root object (not wrapped under certificate). Use this when you have a complete new record and want a single atomic transaction.

PUT vs PATCH: PUT — one tx, all fields required. PATCH — one tx per changed field (fixed order); use when you only adjust a few fields, with the tradeoff of multiple transactions and the last txHash returned (see PATCH).

Auth

Same as POST mint: master or minter x-api-key when auth is enabled; read403; 401 if missing/invalid.

Request

  • Headers: Content-Type: application/json
  • Path
ParamNotes
:addressCertificate contract address
:tokenIdDecimal integer token id

Body (JSON) — full certificate tuple; every field in Certificate metadata fields must be present (same validation as mint’s inner object).

json
{
  "registration_date": 1710892800,
  "delivery_correlative": "2024-001",
  "participant_names": "Ada",
  "participant_last_names": "Lovelace",
  "course_name": "Intro",
  "hours_number": 40,
  "sessions_number": 10,
  "issuing_institution": "Example University",
  "image_url": "https://example.com/img.png",
  "certificate_url": "https://example.com/cert.pdf"
}

Semantics

  • One transaction; waits for receipt before responding.

Responses

200 OK

json
{ "txHash": "0x…" }

Errors

StatusSituation
400Invalid JSON, invalid address/tokenId, missing/invalid certificate fields
401 / 403Same as POST mint
500 / 502Signer/RPC failure or contract revert

Example

bash
curl -sS -X PUT "$BASE_URL/certificates/0xYourCertificate/tokens/0" \
  -H "Content-Type: application/json" \
  -H "x-api-key: $MINTER_KEY" \
  -d '{
    "registration_date": 1710892800,
    "delivery_correlative": "2024-001",
    "participant_names": "Ada",
    "participant_last_names": "Lovelace",
    "course_name": "Intro",
    "hours_number": 40,
    "sessions_number": 10,
    "issuing_institution": "Example University",
    "image_url": "https://example.com/img.png",
    "certificate_url": "https://example.com/cert.pdf"
  }'

PATCH /certificates/:address/tokens/:tokenId

Purpose

Partial update of certificate fields. Each present allowed key triggers a separate onchain transaction (field-specific update_certificate_* methods). The response txHash is from the last successful transaction only; updatedFields counts how many txs were sent.

Auth

Same as POST mint.

Request

  • Headers: Content-Type: application/json
  • Path — same as PUT.

Body (JSON) — only keys you want to change. Keys must be from Certificate metadata fields. Unknown keys are ignored (they do not count toward updates).

Semantics

  • Order of application (and thus of transactions):

    registration_datedelivery_correlativeparticipant_namesparticipant_last_namescourse_namehours_numbersessions_numberissuing_institutionimage_urlcertificate_url.

  • Multi-transaction caveat: If one step reverts after earlier steps succeeded, earlier changes remain onchain; the API returns an error for the failing step without rolling back prior txs. Retry or reconcile using GET …/certificate.

  • txHash in the success body is only the final tx in the sequence.

Responses

200 OK

json
{
  "txHash": "0x…",
  "updatedFields": 2
}

Errors

StatusSituation
400Invalid JSON, not a JSON object, invalid field values, or no recognized fields to update: { "message": "No certificate fields to update" }
401 / 403Same as POST mint
500 / 502Mid-sequence revert (after some txs may have mined), signer/RPC failure

Example

bash
curl -sS -X PATCH "$BASE_URL/certificates/0xYourCertificate/tokens/0" \
  -H "Content-Type: application/json" \
  -H "x-api-key: $MINTER_KEY" \
  -d '{
    "course_name": "Advanced topics",
    "hours_number": 48
  }'

GET /certificates/:address/tokens/:tokenId/certificate

Reads certificate(tokenId). 200: JSON with numeric fields as decimal strings.


GET /certificates/:address/balance-of/:account

200: { "balance": "1" }


GET /certificates/:address/owner-of/:tokenId

200: { "owner": "0x…" }


GET /certificates/:address/approved/:tokenId

ERC-721 getApproved. 200: { "approved": "0x…" }


GET /certificates/:address/token-uri/:tokenId

ERC-721 tokenURI. Equivalent to GET /certificates/:address/tokens/:tokenId/tokenURI below.

200: { "tokenURI": "…" }


GET /certificates/:address/tokens/:tokenId/tokenURI

200: { "tokenURI": "…" }


GET /certificates/:address/total-supply

200: { "totalSupply": "10" }


GET /certificates/:address/token-by-index/:index

200: { "tokenId": "5" }


GET /certificates/:address/token-of-owner-by-index/:owner/:index

200: { "tokenId": "5" }400 if owner is invalid.


GET /certificates/:address/name

200: { "name": "My Cohort" }


GET /certificates/:address/symbol

200: { "symbol": "CERT" }


GET /certificates/:address/owner

Contract owner(). 200: { "owner": "0x…" }


GET /certificates/:address/is-approved-for-all/:ownerParam/:operator

200: { "isApprovedForAll": true }


GET /certificates/:address/is-minter/:account

200: { "isMinter": false }


GET /certificates/:address/nonces/:tokenId

200: { "nonce": "0" }


Errors

400 — validation, invalid address, bad JSON, empty PATCH body, etc.

401 / 403 — API key rules on /certificates when auth is enabled (overview).

500 / 502 — signer or RPC issues, reverted waited transactions, etc. Shape: { "message": "…" }.

Mutating routes (POST, PUT, PATCH) document additional status codes in their sections above.

Released under License.