Overview
A digital key is a strictly one-time link. You mint one immediately after issuing a certificate or reward to an end user. The platform sends the link to their WhatsApp and returns it to you, so you can include it in your own confirmation email as well.
The first time the link is opened it reveals the key and the reward, and offers a button that sends the end user to your redeem URL. From that moment the link is dead — a second open, on any device, is refused. Only a hash of the token is ever stored.
Your price packages live in the merchant portal. Your website reads them through the API, so re-pricing or adding a package goes live without a deploy.
Base URL https://api.crotton.com
Authentication
Send the key on every request. Without a valid key nothing is returned — the response is 401.
Authorization: Bearer dk_live_… # preferred
X-Api-Key: dk_live_… # also accepted
Keep the key server-side only. Rotate it at any time in the portal under API & integration — the previous key stops working immediately and the new one is emailed to the merchant contact.
curl -s https://api.crotton.com/api/v1/me \
-H "Authorization: Bearer $DIGITAL_KEY_API_SECRET"
# 200 {"merchant":{"slug":"yourcompany","name":"YOUR COMPANY", ...}}
curl -s https://api.crotton.com/api/v1/me
# 401 {"error":"unauthorized","message":"API secret key required."}
GET/api/v1/packages
Your active packages in display order. Cache for up to a minute on your side.
{
"merchant": { "slug": "yourcompany", "name": "YOUR COMPANY", "rewardUnit": "tokens" },
"packages": [
{
"id": "clx…", "slug": "pro", "name": "Pro", "tierSlug": "pro",
"priceCents": 10000, "currency": "EUR",
"rewardAmount": 1500, "bonusPercent": 50,
"tagline": "1,500 tokens - best value", "highlight": false, "sortOrder": 20,
"updatedAt": "2026-08-25T12:00:00.000Z"
}
],
"generatedAt": "2026-08-25T12:00:05.000Z"
}
| Field | Meaning |
| slug | Stable route slug for your checkout, lower-case with dashes. “custom” is reserved. |
| tierSlug | Which certificate tier your site issues this package as: standard, pro, studio or custom. |
| priceCents | Price in minor units. Your site should treat this as authoritative. |
| rewardAmount | Total reward, in tokens or credits, that the end user receives. |
| bonusPercent | Display-only bonus share, for example 50 renders as “+50% bonus”. May be null. |
| highlight | Feature this package with accent styling. |
POST/api/v1/keys
Issue a one-time digital key. Call it right after the end user’s certificate has been issued.
curl -s -X POST https://api.crotton.com/api/v1/keys \
-H "Authorization: Bearer $DIGITAL_KEY_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"certificateId": "YCO-2026-7Q2PL",
"holderName": "Jane Borg",
"holderEmail": "jane@example.com",
"whatsappPhone": "+35699123456",
"reward": { "amount": 1500, "unit": "tokens", "label": "1,500 tokens" },
"packageName": "Pro",
"amountCents": 10000,
"txnRef": "TXN-8H2KD0QA",
"mandateRef": "MANDATE-K3F9A2QX",
"redeemUrl": "https://yourcompany.com/certificate/YCO-2026-7Q2PL",
"merchantReference": "order_01HZY…"
}'
| Field | Notes |
| certificateId | Required. Your identifier for the certificate or reward the key unlocks. |
| whatsappPhone | Required. E.164 with country code. Spaces and dashes accepted; a 00 prefix is normalised. |
| reward.amount | Required. Integer reward total shown on the key page. |
| reward.unit / label | Optional. Defaults to the merchant reward unit and a generated label. |
| redeemUrl | Optional. Where the continue button sends the end user. Defaults to your redeem URL template; the key id is appended as ?dk=<keyId>. |
| reissueOf | Optional. A previous key id to revoke before minting this one, for a re-send or a corrected number. At most one live link exists per certificate this way. |
| Optional context | holderName, holderEmail, packageName, amountCents, currency, txnRef, mandateRef, merchantReference — shown on the key page and your dashboard. |
Response 201
{
"key": {
"id": "b7d3…", "ref": "DK-7Q2PL0MC",
"url": "https://crotton.com/redeem/3f9a…64 hex…",
"expiresAt": "2026-08-28T12:00:00.000Z", "status": "issued",
"redeemUrl": "https://yourcompany.com/certificate/YCO-2026-7Q2PL?dk=b7d3…",
"whatsapp": { "status": "sent", "error": null }
}
}
whatsapp.status is sent, failed — the error explains why, for example the number is not on WhatsApp — or skipped. In every case, put key.url in your own confirmation email as well.
GET/api/v1/keys/{id}
Lifecycle state. Poll this, or call it when the end user lands on your certificate page, before allowing redemption.
{
"key": {
"id": "b7d3…", "ref": "DK-7Q2PL0MC", "status": "opened",
"certificateId": "YCO-2026-7Q2PL",
"openedAt": "2026-08-25T12:07:41.000Z", "redeemedAt": null, "revokedAt": null,
"expiresAt": "2026-08-28T12:00:00.000Z", "createdAt": "2026-08-25T12:00:05.000Z",
"whatsapp": { "status": "sent", "error": null },
"merchantReference": "order_01HZY…"
}
}
GET /api/v1/keys?certificateId=… lists your keys, latest first, maximum 100.
POST/api/v1/keys/{id}/redeemed
Report that the certificate behind the key was redeemed on your site. Closes the loop on the dashboard: issued, then opened, then redeemed. The body is empty; the response is 200 {"ok":true}.
POST/api/v1/keys/{id}/revoke
Kill an unopened key, for a refund or a wrong number. An opened key cannot be revoked, because it was used. Returns 409 if the key is not in the issued state.
GET/api/v1/me
Identifies the merchant behind the key. The simplest connectivity test.
Key lifecycle
issued ──(end user opens the link ONCE)──▶ opened ──(you call /redeemed)──▶ redeemed
│
├──(TTL passes, default 72 h)──▶ expired
└──(/revoke or reissueOf)──────▶ revoked
Opening is an atomic conditional update on the platform: exactly one request ever receives the key. The page consumes the link with a script call once it is in front of the holder, so mail scanners and chat link-previewers that merely fetch the URL do not burn it.
Integration checklist
- Store DIGITAL_KEY_API_URL and DIGITAL_KEY_API_SECRET server-side.
- Render your pricing from GET /api/v1/packages, cached around 60 seconds, and charge the package priceCents.
- Collect the end user’s WhatsApp number on your payment step. It is compulsory.
- After issuing the certificate, call POST /api/v1/keys and put key.url in your confirmation email. Do not link the end user straight to redemption.
- On your certificate page, allow the claim only when GET /api/v1/keys/{id} reports opened.
- After redemption, call POST /api/v1/keys/{id}/redeemed.
Errors
| Status | Meaning |
| 400 invalid_request | Validation failed. The message names the field. |
| 401 unauthorized | Missing, malformed, unknown or rotated API key. Nothing is returned. |
| 403 merchant_suspended | The merchant account is suspended by the platform administrator. |
| 404 not_found | No such key for this merchant. |
| 409 invalid_state | The key is not in a state that allows the action. |
| 429 rate_limited | Slow down and honour Retry-After. |
Every error body is { "error": "<code>", "message": "<human readable>" }
Questions about onboarding or access?
Sign in to the portal, or ask us to onboard your merchant. The welcome email carries the API key.
Contact us