Developers
Couri API
Certify an email from your own systems, and let anybody check a certification. Every route here is live and answers today.
Certifying needs an API key, created in your dashboard at app.usecouri.com and shown once. Send it as a bearer token: Authorization: Bearer ck_live_... Keys carry the certify and verify scopes. Checking a certification needs no key at all.
Errors come back as JSON with an error code, for example {"error": "session_not_found"}, sometimes with a detail field. A bad or missing key always gives 401 {"error": "unauthorized"} with no further detail. Limits are per minute; responses carry RateLimit headers and 429 {"error": "rate_limited"} when you go over.
Certify an email
Four steps. Initiate with the message, the sender approves it in the Confrmo app, poll until it is approved, then complete to receive the certification and the footer to put in the email.
POST/api/v1/certify/initiateAPI key, certify scope30 requests a minute
Start a certification
senderUserId | string | The sender, who must be an active member of your organisation. |
payload.senderEmail | string | Must match the sender. |
payload.recipients | string[] | At least one. |
payload.subject | string | Optional. |
payload.body | string | Optional. |
payload.attachmentHashes | string[] | SHA-256 of each attachment, lowercase hex. |
payload.action | string | send, forward, reply or reply-all. |
payload.priorCertificationRefs | string[] | Verification IDs this message replies to or forwards. |
curl -X POST https://api.usecouri.com/api/v1/certify/initiate \
-H "Authorization: Bearer $COURI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"senderUserId": "usr_123",
"payload": {
"senderEmail": "alex@example.com",
"recipients": ["sam@example.org"],
"subject": "Completion statement",
"body": "Please find the statement attached.",
"attachmentHashes": [],
"action": "send",
"priorCertificationRefs": []
}
}'
{
"sessionId": "...",
"messageHashPrefix": "3fa91c",
"comparisonValue": "...",
"approval": { "sid": "...", "openUrl": "...", "vcode": "...", "expiresAt": "..." }
}
400 | senderUserId_required, invalid_payload |
403 | sender_not_in_tenant, sender_not_active, sender_email_mismatch, insufficient_scope |
503 | approval_unavailable |
GET/api/v1/certify/{sessionId}/statusAPI key, certify scope30 requests a minute
Poll for the sender's approval
status is pending, approved, denied or expired.
curl https://api.usecouri.com/api/v1/certify/SESSION_ID/status \ -H "Authorization: Bearer $COURI_API_KEY"
{
"sessionId": "...",
"status": "approved",
"expiresAt": "..."
}
404 | session_not_found |
POST/api/v1/certify/{sessionId}/completeAPI key, certify scope30 requests a minute
Issue the certification
If you send the payload again it is hashed and must match what was approved, or the call fails closed with message_hash_mismatch. Put certifiedFooterHtml at the end of the email you send.
curl -X POST https://api.usecouri.com/api/v1/certify/SESSION_ID/complete \ -H "Authorization: Bearer $COURI_API_KEY"
{
"verificationId": "K7QM-4XRT-9PWD",
"vcodeUti": "...",
"vcodeImageUrl": "...",
"issuedAt": "...",
"signature": "...",
"signingKeyId": "...",
"replyAddress": "...",
"certifiedFooterHtml": "..."
}
409 | session_not_approved, session_expired, message_hash_mismatch, approval_already_consumed |
403 | sender_not_linked_to_confrmo, approver_is_not_sender |
POST/api/v1/certify/{sessionId}/cancelAPI key, certify scope30 requests a minute
Cancel a certification that has not been issued
curl -X POST https://api.usecouri.com/api/v1/certify/SESSION_ID/cancel \ -H "Authorization: Bearer $COURI_API_KEY"
{
"sessionId": "...",
"status": "declined"
}
404 | session_not_found |
409 | session_already_certified |
Check a certification
Open to anybody, with no key. A Verification ID is three groups of four characters, such as K7QM-4XRT-9PWD.
GET/api/v1/verify/{verificationId}None300 requests a minute
Everything public about one certification
binding.strength is recorded, displayed or attested. ledger is null until the record is chained.
curl https://api.usecouri.com/api/v1/verify/K7QM-4XRT-9PWD
{
"verificationId": "K7QM-4XRT-9PWD",
"senderDomain": "example.com",
"organisationName": "Example Ltd",
"action": "send",
"messageHash": "...",
"recipientHashRoot": "...",
"recipientScheme": "...",
"recipientLeaves": null,
"vcodeUti": "...",
"issuedAt": "...",
"status": "...",
"binding": { "strength": "attested", "description": "..." },
"ledger": { "blockNumber": 1042, "recordHash": "...", "previousHash": "...", "ledgerMode": "...", "status": "..." }
}
400 | invalid_verification_id |
404 | not_found |
GET/api/v1/verify/{verificationId}/chainNone300 requests a minute
Where the certification sits in the record chain
curl https://api.usecouri.com/api/v1/verify/K7QM-4XRT-9PWD/chain
{
"block": { "blockNumber": 1042, "recordHash": "...", "previousHash": "...", "issuedAt": "..." },
"next": null
}
404 | not_chained |
GET/api/v1/explorer/searchNone300 requests a minute
Find a certification by its exact Verification ID
q | string | An exact Verification ID. Nothing else is searchable. |
curl "https://api.usecouri.com/api/v1/explorer/search?q=K7QM-4XRT-9PWD"
{
"results": [
{ "verificationId": "K7QM-4XRT-9PWD", "senderDomain": "example.com", "issuedAt": "...", "status": "..." }
]
}
400 | query_must_be_a_verification_id |
Service
GET/api/v1/healthNone
Liveness check
curl https://api.usecouri.com/api/v1/health
{
"status": "ok",
"service": "api-gateway",
"version": "0.1.0"
}