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
  }'
FieldRequiredDescription
credential_type_idYesUUID of the credential type to issue
identity_idYesUUID of the target identity (CCID)
external_unique_idNoYour own reference identifier for this credential
expires_atNoUnix 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>"
ParameterDescription
credential_type_idFilter by credential type
identity_idFilter by identity
entity_idFilter by entity
registry_idFilter by registry
include_onchainsInclude on-chain deployment details in the response
pagePage number (default: 1)
page_sizeResults 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_at when 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
  }'
FieldRequiredDescription
external_unique_idYesUpdated reference identifier
expires_atNoNew 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"
  }'

Get the latest Chainlink content straight to your inbox.