Managing Credentials
Credentials are attestations that a cross-chain identity (CCID) holds a specific qualification — for example, KYC verification, accredited investor status, or sanctions clearance. Each credential links a credential type to an identity and is recorded on-chain across every chain where the credential registry is deployed.
This guide covers the full credential lifecycle: issuing, viewing, updating, expiring, and revoking credentials through the Coordinator API.
Attestation vs. typed credentials
ACE supports two kinds of credentials:
- Attestation-only (the default) — The credential records only that an identity holds a credential of a given type, with no additional data. When you issue one, the on-chain record contains just the credential type hash, the identity (CCID) it is issued to, and an issuance timestamp. Policies verify existence — for example, "does this address have a valid KYC credential?" — without accessing any personally identifiable information (PII).
- Typed — When the credential type is linked to a data schema, the credential also carries structured
credential_data(for example, a jurisdiction code). Policies can then evaluate the contents through a Data Validator, not just the credential's existence.
In both cases, no PII should be stored on-chain — credential data must be a minimal, non-sensitive value (such as an ISO country code) or a hash. For a deeper discussion of credential data and privacy, see Credential Data and Privacy.
Issue a credential
To issue a credential, you need a registered identity and at least one credential type defined in your registry.
Issue a credential with a POST request:
curl -X POST "https://ace.api.chain.link/v1/credentials" \
-H "Authorization: Apikey <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"credential_type_id": "<CREDENTIAL_TYPE_ID>",
"identity_id": "<IDENTITY_ID>",
"external_unique_id": "kyc-2026-04-acme",
"expires_at": 1806883200
}'
| Field | Required | Description |
|---|---|---|
credential_type_id | Yes | UUID of the credential type to issue |
identity_id | Yes | UUID of the target identity (CCID) |
external_unique_id | No | Your own reference identifier for this credential |
expires_at | No | Unix timestamp (integer); omit for a non-expiring credential |
Issue a credential with data
When the credential type is linked to a data schema, include a credential_data field. The value must match the schema — for the ISO 3166-1 alpha-2 country code schema, that is a JSON array of two-letter country codes. ACE validates the data against the schema and encodes it on-chain.
curl -X POST "https://ace.api.chain.link/v1/credentials" \
-H "Authorization: Apikey <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"credential_type_id": "<CREDENTIAL_TYPE_ID>",
"identity_id": "<IDENTITY_ID>",
"credential_data": ["US"],
"external_unique_id": "jurisdiction-2026-04-acme",
"expires_at": 1806883200
}'
Once issued, the credential data can be enforced at transaction time by attaching a Data Validator to the credential source of an identity-validation policy.
Issue credentials during identity creation
You can issue credentials inline when registering a new identity by including a credentials array in the POST /identities request body. This is useful when you have completed verification before registration and want to create the identity and its credentials in a single call.
curl -X POST "https://ace.api.chain.link/v1/identities" \
-H "Authorization: Apikey <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"title": "Acme Corp Treasury",
"entity_id": "acme-corp-001",
"registry_id": "<REGISTRY_ID>",
"onchain_identities": [
{
"chain_selector": "16015286601757825753",
"address": "0x1234567890abcdef1234567890abcdef12345678"
}
],
"credentials": [
{
"credential_type_id": "<CREDENTIAL_TYPE_ID>",
"external_unique_id": "kyc-2026-04-acme",
"expires_at": 1806883200
},
{
"credential_type_id": "<CREDENTIAL_TYPE_ID_2>"
}
]
}'
Each entry in the credentials array follows the same schema as the standalone POST /credentials endpoint, except that identity_id is inferred from the identity being created. See Managing Identities for the full identity creation reference.
View and filter credentials
List credentials with a GET request. All query parameters are optional:
curl "https://ace.api.chain.link/v1/credentials?credential_type_id=<CREDENTIAL_TYPE_ID>&page=1&page_size=25" \
-H "Authorization: Apikey <API_KEY>"
| Parameter | Description |
|---|---|
credential_type_id | Filter by credential type |
identity_id | Filter by identity |
entity_id | Filter by entity |
registry_id | Filter by registry |
include_onchains | Include on-chain deployment details in the response |
page | Page number (default: 1) |
page_size | Results per page |
To retrieve a single credential by its ID:
curl "https://ace.api.chain.link/v1/credentials/<CREDENTIAL_ID>" \
-H "Authorization: Apikey <API_KEY>"
Credential expiration
The expires_at field controls whether a credential has a limited validity period.
- No expiration — Omit
expires_atwhen issuing. The credential remains valid indefinitely until explicitly archived. - With expiration — Provide a Unix timestamp (integer). Once the timestamp passes, policy checks that require this credential type will treat the credential as invalid.
To renew an expiring credential, update it with a new expires_at value (see the next section). Alternatively, you can archive the expired credential and issue a new one.
Update a credential
You can update a credential's external_unique_id and expires_at fields. You cannot change the credential type or the associated identity — to change either, archive the credential and issue a new one.
Update a credential with a PUT request:
curl -X PUT "https://ace.api.chain.link/v1/credentials/<CREDENTIAL_ID>" \
-H "Authorization: Apikey <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"external_unique_id": "kyc-2026-04-acme-renewed",
"expires_at": 1838419200
}'
| Field | Required | Description |
|---|---|---|
external_unique_id | Yes | Updated reference identifier |
expires_at | No | New expiration timestamp; omit to leave unchanged |
You can also perform a partial update with PATCH. The PATCH endpoint accepts external_unique_id and expires_at independently, but you cannot combine field updates with a status change in the same request:
curl -X PATCH "https://ace.api.chain.link/v1/credentials/<CREDENTIAL_ID>" \
-H "Authorization: Apikey <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"expires_at": 1838419200
}'
Revoke (archive) a credential
Archiving a credential removes it from the on-chain credential registry. After archival, policy contracts will no longer see this credential — any policy that requires it (such as the Credential Registry Identity Validator) will reject transactions from the associated addresses.
Common reasons to revoke a credential:
- KYC verification expired or failed re-verification
- Sanctions status changed
- Accreditation lapsed
- Entity relationship terminated
Archive a credential with a PATCH request:
curl -X PATCH "https://ace.api.chain.link/v1/credentials/<CREDENTIAL_ID>" \
-H "Authorization: Apikey <API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"status": "archived"
}'
Related resources
- Cross-Chain Identity — CCID model, credential registries, and the attestation lifecycle
- Managing Identities — register and manage CCIDs and their on-chain address mappings
- Managing Credential Types — create and organize the credential categories your registry supports
- Credential Registry Identity Validator Policy — the policy that checks credentials at transaction time
- Managing Data Validators — enforce rules on credential data at transaction time
- Beta Scope — current scope and limitations