Appearance
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
| Method | Path | Signer required |
|---|---|---|
POST | /certificates/:address/mint | Yes |
PUT | /certificates/:address/tokens/:tokenId | Yes |
PATCH | /certificates/:address/tokens/:tokenId | Yes |
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.
| Field | Type (request) | Notes |
|---|---|---|
registration_date | integer or decimal string | |
hours_number | integer or decimal string | |
sessions_number | integer or decimal string | |
delivery_correlative | string | |
participant_names | string | |
participant_last_names | string | |
course_name | string | |
issuing_institution | string | |
image_url | string | |
certificate_url | string |
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
| Param | Notes |
|---|---|
:chainId | Must be 42161 (Arbitrum One). Other values → 400. |
:addressOrCertificateId | Either 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. |
:tokenId | Non-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_urifromPOST /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-keywith master or minter; read keys get 403 onPOST.- 401 — missing or invalid key.
Request
- Headers:
Content-Type: application/json - Path
| Param | Notes |
|---|---|
:address | Certificate contract address |
Body (JSON)
| Field | Type | Required | Notes |
|---|---|---|---|
to | string | Yes | Recipient; aliases recipient, owner (first present wins). Must be a valid address. |
certificate | object | Yes* | One of certificate, data, or certificateData (same shape). See Certificate metadata fields. |
data | object | Yes* | Alternative to certificate. |
certificateData | object | Yes* | Alternative to certificate. |
certificateAddress | string | No | If set, must match :address. |
Semantics
- Single onchain transaction submitted by the service; response
transaction.statusreflects the receipt (e.g.success). - If the receipt has no matching
CertificateMintedevent for this certificate, the API returns 500 (see Errors).
Responses
200 OK
| Field | Type | Notes |
|---|---|---|
mint.tokenId | string | Minted token id |
mint.certificateAddress | string | Same as :address |
mint.mintedTo | string | Request recipient |
transaction.hash | string | Tx hash |
transaction.blockNumber | string | Block number |
transaction.status | string | Receipt status |
transaction.gasUsed | string | Gas used |
json
{
"mint": {
"tokenId": "1",
"certificateAddress": "0x…",
"mintedTo": "0x…"
},
"transaction": {
"hash": "0x…",
"blockNumber": "123456789",
"status": "success",
"gasUsed": "210000"
}
}Errors
| Status | Situation |
|---|---|
| 400 | Invalid JSON, missing certificate tuple / recipient, address mismatch, invalid certificateAddress or to |
| 401 | API key missing or invalid when auth is enabled |
| 403 | Read-only API key |
| 500 | Mint reverted, RPC/signer failure, or receipt ok but no matching CertificateMinted log: { "message": "Mint succeeded but CertificateMinted event was not found" } |
| 502 | Upstream 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; read → 403; 401 if missing/invalid.
Request
- Headers:
Content-Type: application/json - Path
| Param | Notes |
|---|---|
:address | Certificate contract address |
:tokenId | Decimal 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
| Status | Situation |
|---|---|
| 400 | Invalid JSON, invalid address/tokenId, missing/invalid certificate fields |
| 401 / 403 | Same as POST mint |
| 500 / 502 | Signer/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_date→delivery_correlative→participant_names→participant_last_names→course_name→hours_number→sessions_number→issuing_institution→image_url→certificate_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.txHashin the success body is only the final tx in the sequence.
Responses
200 OK
json
{
"txHash": "0x…",
"updatedFields": 2
}Errors
| Status | Situation |
|---|---|
| 400 | Invalid JSON, not a JSON object, invalid field values, or no recognized fields to update: { "message": "No certificate fields to update" } |
| 401 / 403 | Same as POST mint |
| 500 / 502 | Mid-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.