The Attestation API is the core of the Truthlocks platform. It lets you mint verifiable credentials, verify their authenticity and status, revoke them when necessary, and manage the entire attestation lifecycle at scale. This post walks through every major endpoint with practical examples in cURL, JavaScript, and Python.
Authentication
All API requests require an API key passed in the Authorization header. You can generate keys in the Truthlocks Console under Settings > API Keys. Keys are scoped to a specific environment (sandbox or production) and can be restricted to specific operations.
Authorization: Bearer tl_live_sk_7f8g9h0j1k2l3m4n5o6p
Sandbox keys use the prefix tl_test_sk_ and point to the sandbox environment where no real credentials are issued. We recommend using sandbox keys during development and testing.
Minting an Attestation
The POST /v1/attestations endpoint creates a new attestation. You provide the credential type, the subject's identifier, and the claims.
cURL
curl -X POST https://api.truthlocks.com/v1/attestations \
-H "Authorization: Bearer tl_live_sk_..." \
-H "Content-Type: application/json" \
-d '{
"credential_type": "professional_license",
"subject_did": "did:truthlock:holder_abc123",
"claims": {
"license_type": "CPA",
"license_number": "CPA-2025-78901",
"state": "California",
"issued_date": "2025-06-15",
"expiry_date": "2027-06-15"
},
"expiry": "2027-06-15T00:00:00Z"
}'
JavaScript
import { TruthlockClient } from '@truthlocks/sdk';
const client = new TruthlockClient({
apiKey: process.env.TRUTHLOCK_API_KEY,
});
const attestation = await client.attestations.mint({
credentialType: 'professional_license',
subjectDid: 'did:truthlock:holder_abc123',
claims: {
license_type: 'CPA',
license_number: 'CPA-2025-78901',
state: 'California',
issued_date: '2025-06-15',
expiry_date: '2027-06-15',
},
expiry: '2027-06-15T00:00:00Z',
});
console.log(attestation.id); // att_8f3k2n4m5p6q
Python
from truthlocks import TruthlockClient
client = TruthlockClient(api_key="tl_live_sk_...")
attestation = client.attestations.mint(
credential_type="professional_license",
subject_did="did:truthlock:holder_abc123",
claims={
"license_type": "CPA",
"license_number": "CPA-2025-78901",
"state": "California",
"issued_date": "2025-06-15",
"expiry_date": "2027-06-15",
},
expiry="2027-06-15T00:00:00Z",
)
print(attestation.id) # att_8f3k2n4m5p6q
The response includes the attestation ID, the issuer's signature, and a transparency log inclusion proof.
Verifying an Attestation
The POST /v1/attestations/verify endpoint checks an attestation's signature, revocation status, expiry, issuer trust level, and transparency log inclusion in a single call.
curl -X POST https://api.truthlocks.com/v1/attestations/verify \
-H "Authorization: Bearer tl_live_sk_..." \
-H "Content-Type: application/json" \
-d '{"attestation_id": "att_8f3k2n4m5p6q"}'
The response includes a structured result:
{
"valid": true,
"attestation_id": "att_8f3k2n4m5p6q",
"checks": {
"signature": "valid",
"revocation": "not_revoked",
"expiry": "active",
"issuer_trust_level": "enhanced",
"transparency_log": "included"
},
"verified_at": "2026-01-12T14:30:00Z"
}
Batch Operations
For high-volume workflows, use the batch endpoints. POST /v1/attestations/batch/mint accepts up to 100 attestations per request, and POST /v1/attestations/batch/verify accepts up to 1,000 attestation IDs.
const results = await client.attestations.batchVerify({
attestationIds: [
'att_8f3k2n4m5p6q',
'att_2j5l7n9p1r3t',
'att_4k6m8o0q2s4u',
],
});
results.forEach(r => {
console.log(`${r.attestationId}: ${r.valid ? 'valid' : 'invalid'}`);
});
Batch operations are processed concurrently on the server. The total latency is roughly equivalent to a single operation, making batch endpoints significantly more efficient than sequential single-item calls.
Revoking an Attestation
The POST /v1/attestations/{id}/revoke endpoint marks an attestation as revoked. Revocation is immediate — subsequent verification requests will return "revocation": "revoked". Revocation is irreversible.
curl -X POST https://api.truthlocks.com/v1/attestations/att_8f3k2n4m5p6q/revoke \
-H "Authorization: Bearer tl_live_sk_..." \
-H "Content-Type: application/json" \
-d '{"reason": "license_expired"}'
Revocation reasons are freeform strings but we recommend using consistent values from a controlled vocabulary (e.g., license_expired, key_compromise, holder_request, issuer_ceased_operations).
Webhooks
Webhooks let you receive real-time notifications when attestation events occur. Configure webhook endpoints in the console under Settings > Webhooks. Supported events include:
attestation.minted— a new attestation was created.attestation.verified— an attestation was verified by a third party.attestation.revoked— an attestation was revoked.attestation.expired— an attestation reached its expiry date.issuer.trust_level_changed— an issuer's trust level was modified.
Webhook payloads are signed with HMAC-SHA256 using your webhook secret. Always verify the signature before processing the payload:
import crypto from 'crypto';
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Error Handling
The API uses standard HTTP status codes and returns structured error responses:
{
"error": {
"code": "attestation_not_found",
"message": "No attestation found with ID att_invalid",
"request_id": "req_9a8b7c6d5e4f"
}
}
Common error codes include authentication_failed (401), insufficient_permissions (403), attestation_not_found (404), validation_error (422), and rate_limit_exceeded (429). The request_id is useful for debugging — include it when contacting support.
Rate Limits
Rate limits depend on your plan:
- Starter: 100 requests/minute, 10,000 requests/day.
- Professional: 1,000 requests/minute, 100,000 requests/day.
- Enterprise: Custom limits based on your contract.
Rate limit headers are included in every response:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1704067200
When you hit the rate limit, the API returns a 429 status with a Retry-After header indicating when you can resume requests. Our SDKs handle rate limit retries automatically with exponential backoff.
Next Steps
This guide covers the most common API operations. For the full API reference — including credential type management, issuer administration, and transparency log queries — visit the API documentation. If you have questions or need help with your integration, reach out through the console or join our developer community.
