# Changelog (/docs/api-reference/changelog)
A new API version is released when we introduce a **backwards-incompatible** change to the API. Please see all the available versions in the table below:
| Version | Breaking changes |
| :----------- | :----------------------------------- |
| `2022-10-26` | The Certifier API has been released. |
# Error Handling (/docs/api-reference/error-handling)
All API endpoints contain a `code` and `message` within the error responses, though some API endpoints extend the error object to contain other information. Each endpoint that does this will be documented in their appropriate section.
While we recommend that you write error messages that fit your needs and provide your users with the best experience, our `message` fields are designed to be neutral, not contain sensitive information, and can be safely passed down to user interfaces.
```json
{
"error": {
"code": "bad_request",
"message": "A description of the error that just occurred"
}
}
```
Certifier API supports the following codes:
| HTTP status code | Certifier code | Description |
| :----------------- | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400 | `missing_version` | The request is missing the required `Certifier-Version` header. See [versioning](/docs/api-reference/versioning). |
| 400 | `invalid_version` | The `Certifier-Version` header's value is invalid. See [versioning](/docs/api-reference/versioning). |
| 400 | `invalid_json` | The request body can not be decoded as JSON. |
| 400 | `validation_error` | This usually occurs because of a missing or malformed parameter. Check the documentation and the syntax of your request and try again. |
| 401 | `unauthorized` | A valid authentication token was not provided with the request, so the API could not associate a user with the request. |
| 402 | `payment_required` | The request was valid, but the queried object or object mutation specified in the request is only available to premium organizations and workspaces. |
| 403 | `forbidden` | The authentication and request syntax was valid but the server is refusing to complete the request. This can happen if you try to read or write to objects or properties that the user does not have access to. |
| 404 | `not_found` | Either the request method and path supplied do not specify a known action in the API, or the object specified by the request does not exist. |
| 409 | `conflict` | The request could not be completed, potentially due to a data collision. Make sure the parameters are up to date and try again. |
| 429 | `rate_limited` | You have exceeded one of the enforced rate limits in the API. See the [documentation on rate limiting](/docs/api-reference/rate-limits) for more information. |
| 500, 502, 503, 504 | `internal_server_error` | There was a problem on Certifier's end. |
In the future, Certifier may add some new codes. We advise you to rely on the Certifier's `code` property rather than
on HTTP status only to distinguish between errors. Also, we recommend you not to write code that relies on the
`message` property heavily since it could change in the future.
# Pagination (/docs/api-reference/pagination)
When the API response includes an array of records, a `pagination` object is returned when the total number of records present is greater than the `limit` per request. The default value of this `limit` is 20 but it can be changed by passing a value to the query parameter `limit` when the request is made. The maximum possible value of `limit` is 100.
You can then use the `pagination` object to make additional requests and obtain all the records.
The pagination object is structured as shown in the example below:
```json
{
"pagination": {
"prev": null,
"next": "bmV4dCQkMDFrN2VlNWEyMTE1NWJoZHh4emZoZGpjZWgkJDAxazdlZTVhMjExNTViaGR4eHpmaGRqY2Vo" // example
}
}
```
In order to obtain the records for the next batch, perform the following actions:
1. Send a request to the same API endpoint.
2. Include the query parameter `cursor` with a value equal to the value of `next` returned in the previous request.
3. Repeat this sequence until the pagination object has a `next` value of `null`.
# Rate Limits (/docs/api-reference/rate-limits)
Rate-limited requests will return a `rate_limited` error code (HTTP response status 429). The rate limit for incoming requests is an average of 3 requests per second. Some bursts beyond the average rate are allowed.
Integrations should accommodate variable rate limits by handling HTTP 429 responses and respecting the `Retry-After` response [header value](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After), which is set as an integer number of seconds. Requests made after waiting this minimum amount of time should no longer be rate limited.
Alternatively, rate limits can be accommodated by backing off (or slowing down) the speed of future requests. A common way to implement this is using one or several queues for pending requests, which can be consumed by sending requests as long as Certifier does not respond with an HTTP 429.
In the future, Certifier plans to adjust rate limits to balance for demand and reliability. Certifier may also
introduce distinct rate limits for workspaces in different pricing plans.
# Versioning (/docs/api-reference/versioning)
The Certifier API is versioned. Our API versions are named for the date the version is released, for example, our latest version is `2022-10-26`.
You set the version by including a `Certifier-Version` header.
```bash
curl https://api.certifier.io/v1/me \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Certifier-Version: 2022-10-26"
```
A new API version is released when we introduce a **backwards-incompatible** change to the API.
The `Certifier-Version` header is required and must be added to all requests. This best ensures the Certifier API is
consistent with what your code expects.
# Webhooks (/docs/api-reference/webhooks)
## Introduction [#introduction]
Webhooks allow you to subscribe to changes in Certifier and receive real-time HTTP requests to a specified URL when those changes occur.
This mechanism is ideal for real-time data synchronization (e.g., ETL pipelines) and event-driven automation (e.g., Certifier's Zapier integration relies on webhooks).
## Creating Webhooks [#creating-webhooks]
You can create, view, and manage your webhooks in the Certifier dashboard:
[Settings → Developers → Webhooks](https://app.certifier.io/settings/workspace/developers/webhooks)
## Webhook Payloads [#webhook-payloads]
Webhooks are sent as `POST` requests with a JSON payload. The payload structure depends on the event type.
### Example Payload [#example-payload]
```json
{
"id": "01jmyw7nvtk2372kwn572mwfq7",
"type": "credential.created",
"createdAt": "2025-02-25T15:39:59.680Z",
"data": {
"resource": {
"id": "01jmyw4zyyrtyntqcevy2zmbnf",
"type": "credential"
}
}
}
```
## Webhook Security [#webhook-security]
To ensure authenticity, all webhook requests are signed using a cryptographic signature.
Never process webhook payloads without validating the signature.
### Signature Verification [#signature-verification]
Each webhook request includes an `X-Webhook-Signature` header, which contains a SHA256 HMAC signature of the request payload, signed with your webhook secret.
### How to Verify Signatures [#how-to-verify-signatures]
1. Retrieve the `X-Webhook-Signature` header.
2. Compute the HMAC SHA256 signature using your webhook secret and the request payload.
3. Compare your computed signature with the received signature using a timing-safe comparison.
#### Example in Node.js [#example-in-nodejs]
```javascript
const crypto = require("crypto");
/**
* Verifies the authenticity of a webhook request using HMAC SHA256.
* @param {string} payload - The raw webhook payload.
* @param {string} receivedSignature - The received X-Webhook-Signature header.
* @param {string} secret - Your webhook secret.
* @returns {boolean} - Returns true if the signature is valid, false otherwise.
*/
function verifyWebhookSignature(payload, receivedSignature, secret) {
if (!receivedSignature) {
console.error("Missing webhook signature");
return false;
}
try {
const hmac = crypto.createHmac("sha256", secret);
hmac.update(payload, "utf8");
const expectedSignature = hmac.digest("hex");
return crypto.timingSafeEqual(Buffer.from(receivedSignature, "hex"), Buffer.from(expectedSignature, "hex"));
} catch (error) {
console.error("Error verifying webhook signature:", error);
return false;
}
}
// Usage example
const webhookPayload = JSON.stringify(request.body);
const receivedSignature = request.headers["x-webhook-signature"];
const webhookSecret = "your_webhook_secret";
if (!verifyWebhookSignature(webhookPayload, receivedSignature, webhookSecret)) {
throw new Error("Invalid webhook signature");
}
```
## Webhook Event Types [#webhook-event-types]
Webhooks follow a `resource.action` naming pattern. The available events are:
| Event | Description |
| -------------------- | ----------------------------- |
| `credential.created` | A credential has been created |
| `credential.updated` | A credential has been updated |
| `credential.deleted` | A credential has been deleted |
| `credential.issued` | A credential has been issued |
## Best Practices [#best-practices]
Follow these guidelines to ensure reliable webhook handling.
* **Verify webhook signatures** to ensure authenticity.
* **Use HTTPS endpoints** for security.
* **Respond within 5 seconds** to avoid timeouts.
* **Process webhooks asynchronously** to improve reliability.
* **Log webhook deliveries and failures** for debugging purposes.
* **Use idempotency mechanisms** to handle duplicate webhook deliveries.
## Retry Mechanism [#retry-mechanism]
If your server fails to respond with a `2xx` HTTP status code, we will retry the delivery using an exponential backoff schedule:
1. 3 minutes after the first failure
2. 6 minutes after the previous retry
3. 30 minutes after the previous retry
4. 1 hour after the previous retry
5. 12 hours after the previous retry
6. 24 hours after the previous retry
7. 48 hours after the previous retry
Retries stop after 7 failed attempts (\~3.5 days total). You can monitor webhook failures in the dashboard.
### Retry Headers [#retry-headers]
* **`X-Retry-Count`**: Indicates the retry attempt number (starts at 1). The initial attempt does not include this header.
## Webhook Technical Details [#webhook-technical-details]
| Property | Value |
| ------------------ | ------------------ |
| Timeout | 5 seconds |
| Maximum Retries | 7 attempts |
| Retry Window | \~3.5 days |
| Required Response | HTTP 2xx |
| Request Method | `POST` |
| Content Type | `application/json` |
| Character Encoding | `UTF-8` |
## Testing Webhooks [#testing-webhooks]
We recommend using [webhook.site](https://webhook.site) or [RequestBin](https://requestbin.com/) to inspect webhook payloads during development.
## Monitoring & Debugging [#monitoring--debugging]
You can track webhook deliveries, failures, and retries in the Certifier dashboard:
[Settings → Developers → Webhooks → Events](https://app.certifier.io/settings/developers/webhooks/events)
## Rate Limiting [#rate-limiting]
Currently, there are no rate limits on webhook deliveries. However, we recommend implementing queue-based processing on your end to handle high event volumes efficiently.
# Introduction (/docs/api-reference)
## Getting Started [#getting-started]
Everything you need to start building with the Certifier API.
Learn how authentication works, how to make your first request, and how to stay up to date with changes.
Make your first authenticated API request in minutes and learn the basic request structure.
Connect your MCP client to Certifier using the `/v1/mcp` endpoint.
## Core Concepts [#core-concepts]
Before diving deeper, familiarize yourself with the core principles that apply to all Certifier API endpoints.
Learn how large result sets are paginated and how to navigate through API responses efficiently.
Understand API rate limits, how they are enforced, and best practices for handling them.
See how API versions are managed and how to ensure compatibility with future updates.
Learn about standard error responses, status codes, and how to properly handle API errors.
View API version history and breaking changes.
Learn about Webhooks and how to subscribe to events.
# MCP Server (/docs/api-reference/mcp-server)
Certifier provides a remote MCP server that lets compatible AI clients interact with your Certifier workspace straight from your chat interface.
**Remote MCP server URL**
`https://api.certifier.io/v1/mcp`
## Features Overview [#features-overview]
With the Certifier MCP server, users can do everything that is possible through the Public API in a familiar environment of a chat interface, including:
* search, list, and inspect existing credentials
* create, issue and send credentials
* create and manage groups (credential templates)
* list and inspect created design templates
## Requirements [#requirements]
To connect successfully, you need:
* a Certifier account with access to at least one workspace
* an MCP client that supports remote MCP over HTTP with OAuth
## Connect the Server [#connect-the-server]
To connect a compatible MCP client:
1. Add the remote MCP server URL: `https://api.certifier.io/v1/mcp`
2. Start the connection flow in your MCP client.
3. Sign in to Certifier if prompted.
4. Choose the workspace you want to authorize.
5. Approve the requested access.
6. Return to your MCP client and start using Certifier tools.
### Claude Code [#claude-code]
To add Certifier MCP server to Claude Code, use this command:
```
claude mcp add --transport http certifier https://api.certifier.io/v1/mcp
```
Authentication flow is then available from within Claude Code, using `/mcp` command and navigating to Certifier MCP.
### Codex [#codex]
To add Certifier MCP server to Codex, use this command:
```
codex mcp add certifier --url https://api.certifier.io/v1/mcp
```
The authentication flow should start automatically.
### OpenCode [#opencode]
To add Certifier MCP server to OpenCode, use this command:
```
opencode mcp add
```
An interactive flow will guide you through the process:
1. Select `Current project`/`Global` depending on your needs
2. We recommend `Certifier` as MCP server name
3. Select `Remote` as MCP server type
4. Enter `https://api.certifier.io/v1/mcp` as server URL
5. Select `Yes` when asked if the server requires OAuth authentication
6. Select `No` when asked if you have a pre-registered client ID
Then start the authentication flow with
```
opencode mcp auth certifier
```
## Authentication [#authentication]
The Certifier MCP server uses the standard OAuth authorization code flow for remote MCP servers.
Certifier publishes discovery documents that compatible clients can use automatically:
* Authorization server metadata: `https://api.certifier.io/.well-known/oauth-authorization-server`
* Protected resource metadata: `https://api.certifier.io/.well-known/oauth-protected-resource/v1/mcp`
Compatible clients can use OAuth discovery and dynamic client registration automatically. You do not need a pre-registered client ID for the standard connection flow.
Certifier supports the `mcp:read` and `mcp:write` scopes. Read-only actions use `mcp:read`; actions that create, update, issue, send, or delete data require `mcp:write`.
## Common Use Cases [#common-use-cases]
Here are some example requests you can make from a compatible MCP client after connecting Certifier.
### 1. Issue and Send a Credential [#1-issue-and-send-a-credential]
```
Issue and email a certificate to Jane Doe at jane@example.com for completing Product Training. Use the "Product Training 2026" group (credential template).
```
This is useful when you want to generate and deliver a credential in one step without leaving the chat.
### 2. Search for Existing Credentials [#2-search-for-existing-credentials]
```
Find credentials issued to recipients from acme.com and show me the most recent ones first.
```
This helps with audits, support requests, and checking whether a credential has already been issued.
### 3. Create a Group (Credential Template) for a New Program [#3-create-a-group-credential-template-for-a-new-program]
```
Create a group (credential template) called "Leadership Academy Spring 2026" and attach the specified certificate and badge design templates.
```
Use this when you are setting up a new course, cohort, event, or certification program in Certifier.
### 4. Inspect Available Design Templates [#4-inspect-available-design-templates]
```
List my available design templates and show me the details for the one best suited for employee certifications.
```
This is helpful when choosing which design template to use before issuing credentials at scale.
### 5. Update or Resend a Credential [#5-update-or-resend-a-credential]
```
Find the credential for Alex Jonhson, update the recipient's name to Alex Johnson, and send it again by email.
```
This is useful for credential corrections, renewals, and support workflows.
## Privacy [#privacy]
When you connect an MCP client to Certifier, that client can send authorized tool requests to Certifier on your behalf for the workspace you approved.
Data sent through the MCP client is processed by Certifier to fulfill your requests and is subject to Certifier's legal terms and privacy practices:
* Privacy Policy: [https://certifier.io/privacy-policy](https://certifier.io/privacy-policy)
* Terms of Service: [https://certifier.io/terms-of-service](https://certifier.io/terms-of-service)
Only connect trusted MCP clients and only send data you want Certifier to process.
## Support [#support]
If you need help connecting or using the Certifier MCP server:
* Support Center: [https://support.certifier.io/](https://support.certifier.io/)
* Email: [support@certifier.io](mailto:support@certifier.io)
When contacting support, include:
* the MCP client you are using
* the workspace involved
* the approximate time of the failed request
* the action you were trying to perform
## Notes [#notes]
* The Certifier MCP server is intended for clients that support remote MCP over HTTP and OAuth.
* Tool availability depends on the data and permissions available in the workspace you authorize.
* If access expires or is revoked, reconnect the server and complete the OAuth flow again.
# Quickstart (/docs/api-reference/quickstart)
## Introduction [#introduction]
Certifier API is a REST-styled interface, providing programmatic access to much of the data in the system. It provides predictable URLs for accessing resources, and uses built-in HTTP features to receive commands and return responses. This makes it easy to communicate from a wide variety of environments: command-line utilities, gadgets, and even the browser URL bar itself.
The API accepts JSON content in requests and returns JSON content in all of its responses, including errors. Only UTF-8 character encoding is supported for both requests and responses.
## Authentication [#authentication]
Certifier API uses access tokens to authenticate requests.
You can view and manage your access tokens in [the Certifier Dashboard](https://app.certifier.io/settings/workspace/developers/access-tokens).
Your access tokens carry many privileges, so be sure to keep them secure! Do not share your secret access tokens in publicly accessible areas such as GitHub, client-side code, and so forth.
Authentication to the API is performed via [HTTP Bearer Auth](https://swagger.io/docs/specification/authentication/bearer-authentication/). Provide your access token in `Authorization` header with `Bearer` auth-scheme and `Certifier-Version` header:
```
Authorization: Bearer
Certifier-Version: 2022-10-26
```
All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.
Access tokens can be created and managed from inside your account settings. Go to the [Settings -> Developers -> Access Tokens](https://app.certifier.io/settings/developers/access-tokens) and create an access token there.
### Generate an Access Token [#generate-an-access-token]
Access tokens are used to authenticate your API requests.
To generate a token:
#### 1. Go to **Settings → Developers** [#1-go-to-settings--developers]
#### 2. Click **Generate Access Token** [#2-click-generate-access-token]
#### 3. Provide a descriptive name [#3-provide-a-descriptive-name]
#### 4. Click **Continue** [#4-click-continue]
#### 5. Copy the token (it will not be visible again) [#5-copy-the-token-it-will-not-be-visible-again]
Now you can use your access token to authenticate and use the Certifier API:
```bash
curl https://api.certifier.io/v1/credentials \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Certifier-Version: 2022-10-26"
```
Treat your tokens like passwords and keep them secret. When working with the API, use tokens as environment variables
instead of hardcoding them into your programs.
# List credentials (/docs/api-reference/credentials/list-credentials)
Use this endpoint to list credentials.
# Create a credential (/docs/api-reference/credentials/create-a-credential)
Use this endpoint to create a new credential.
The credential will be created with status `draft`.
As the next step, use the [Issue Credential](/docs/api-reference/credentials/issue-a-credential) endpoint to issue a draft credential, making it active and accessible in the digital wallet.
If you are looking for an all-in-one solution that simultaneously creates, issues, and sends a credential, check the [Create, Issue and Send a Credential](/docs/api-reference/credentials/create-issue-and-send-a-credential) endpoint.
# Create, issue, and send a credential (/docs/api-reference/credentials/create-issue-and-send-a-credential)
Use this endpoint to issue a credential. It is an all-in-one solution that simultaneously creates, issues, and sends a credential. This endpoint covers most of the basic scenarios.
# Get a credential (/docs/api-reference/credentials/get-a-credential)
Use this endpoint to get a specific credential.
# Update a credential (/docs/api-reference/credentials/update-a-credential)
Use this endpoint to update a credential.
# Delete a credential (/docs/api-reference/credentials/delete-a-credential)
Use this endpoint to permanently delete a credential. This cannot be undone.
# Get credential design templates (/docs/api-reference/credentials/get-credential-design-templates)
Use this endpoint to list the ordered design templates used by a credential and their available previews. Preview URLs are immutable and versioned by a digest (hash) derived from the design template's last update timestamp and the resolved values of all rendered credential attributes.
Preview URLs are immutable and versioned by a digest (hash) derived from:
- the design template’s last update timestamp, and
- the resolved values of all rendered credential attributes
If you modify the design template (layout, fonts, colors, etc.) or any attribute value that appears in the design template, the digest changes and previous preview URLs will not automatically point to the new design template.
To get the newest preview after any change, fetch **Get credential design templates** again and use the newly returned preview URL.
The digest-versioned URL (…/.png or .pdf) is permanent for that exact version and is cache-friendly, making it safe to store or share.
# Issue a credential (/docs/api-reference/credentials/issue-a-credential)
Use this endpoint to issue a draft credential and change the status from `draft` to `issued`.
You can only issue credentials with `status = "draft"`.
As the next step, use the [Send a credential](/docs/api-reference/credentials/send-a-credential) endpoint to send your recipient a published credential.
If you are looking for an all-in-one solution that simultaneously creates, issues, and sends a credential, check the [Create, issue, and send a credential](/docs/api-reference/credentials/create-issue-and-send-a-credential) endpoint.
# Search credentials (/docs/api-reference/credentials/search-credentials)
1. [Introduction](#introduction)
* [Example Request](#example-request)
2. [Filtering](#filtering)
* [Logical Operators](#logical-operators)
* [AND](#and)
* [OR](#or)
* [NOT](#not)
* [Field Types & Comparison Operators](#field-types--comparison-operators)
* [String Fields](#string-fields)
* [UUID Fields](#uuid-fields)
* [Enum Fields](#enum-fields)
* [Date Fields (YYYY-MM-DD)](#date-fields-yyyy-mm-dd)
* [DateTime Fields (ISO 8601)](#datetime-fields-iso-8601)
* [Handling Null Values](#handling-null-values)
* [In Operator Limitation for Null Values](#in-operator-limitation-for-null-values)
3. [Credentials Searchable Fields](#credentials-searchable-fields)
* [Core Fields](#core-fields)
* [Recipient Fields](#recipient-fields)
* [Temporal Fields](#temporal-fields)
4. [Sorting](#sorting)
* [Credentials Sortable Properties](#credentials-sortable-properties)
5. [Pagination](#pagination)
6. [Complete Example](#complete-example)
7. [Tips and Advanced Use](#tips-and-advanced-use)
* [Implicit AND](#implicit-and)
* [Top-Level Logical Operators](#top-level-logical-operators)
* [Filter Condition Objects](#filter-condition-objects)
* [Comparison Operator Expressions](#comparison-operator-expressions)
## Introduction
The Credentials Search API allows you to find and filter credentials using structured queries with logical operators, sorting, and pagination.
A search request payload consists of:
* **`filter`** – to define search conditions using `AND`, `OR`, and `NOT` logical operators.
* **`sort`** – containing `property` and `order`, allowing control over result ordering.
* [Standard pagination](https://developers.certifier.io/reference/pagination) using `cursor` and `limit`.
### Example Request
```
POST /v1/credentials/search
Content-Type: application/json
```
```json
{
"filter": {
"AND": [
{
"status": {
"equals": "issued"
}
},
{
"recipient": {
"name": {
"contains": "John"
}
}
}
]
},
"sort": {
"property": "createdAt",
"order": "desc"
},
"cursor": "some_cursor_value",
"limit": 25
}
```
***
## Filtering
Filter allows complex queries by combining multiple conditions using logical operators.
### Logical Operators
Logical operators define how multiple conditions are combined.
#### `AND`
All conditions must be met.
```json
{
"filter": {
"AND": [
{
"status": {
"equals": "issued"
}
},
{
"recipient": {
"email": {
"endsWith": "@company.com"
}
}
}
]
}
}
```
#### `OR`
At least one condition must be met.
```json
{
"filter": {
"OR": [
{
"status": {
"equals": "draft"
}
},
{
"status": {
"equals": "expired"
}
}
]
}
}
```
#### `NOT`
Excludes records that match the condition.
```json
{
"filter": {
"NOT": [
{
"status": {
"equals": "expired"
}
}
]
}
}
```
Operators can be combined:
```json
{
"filter": {
"AND": [
{
"NOT": [
{
"recipient": {
"email": {
"endsWith": "@company.com"
}
}
}
]
},
{
"recipient": {
"name": {
"startsWith": "John"
}
}
}
]
}
}
```
### Field Types & Comparison Operators
Comparison operators define how field values are filtered.
#### String Fields
```json
{
"recipient": {
"name": {
"contains": "John"
}
}
}
```
* `equals`: Exact match
* `contains`: Partial match
* `startsWith`: Prefix match
* `endsWith`: Suffix match
* `in`: Matches any value in an array
#### UUID Fields
UUID fields (such as `publicId`) only support exact matching. Each value must be a valid UUID — malformed values are rejected.
```json
{
"publicId": {
"equals": "124a8110-1af5-4747-9308-e9d06bd1852a"
}
}
```
* `equals`: Exact match (must be a valid UUID)
* `in`: Matches any value in an array (each must be a valid UUID)
#### Enum Fields
```json
{
"status": {
"in": [
"draft",
"expired"
]
}
}
```
* `equals`: Exact match
* `in`: Matches any value in an array
#### Date Fields (`YYYY-MM-DD`)
```json
{
"issueDate": {
"gte": "2024-01-01"
}
}
```
* `equals`: Exact match
* `lt`: Less than
* `lte`: Less than or equal
* `gt`: Greater than
* `gte`: Greater than or equal
#### DateTime Fields (ISO 8601)
```json
{
"createdAt": {
"gte": "2025-02-21T14:18:33Z"
}
}
```
* `equals`: Exact match
* `lt`: Less than
* `lte`: Less than or equal
* `gt`: Greater than
* `gte`: Greater than or equal
#### Handling Null Values
```json
{
"expiryDate": {
"equals": null
}
}
```
* `{ "expiryDate": { "equals": null } }`: Matches records where the field is `null`.
* `{ NOT: [{ "expiryDate": { "equals": null } }] }`: Excludes records where the field is `null`.
#### `in` Operator Limitation for Null Values
The `in` operator **does not support** `null` values. If you need to match `null` alongside other values, use an `OR` logical statement instead:
```json
{
"filter": {
"OR": [
{
"recipientId": {
"equals": null
}
},
{
"recipientId": {
"in": [
"01jmerb62apgachxwx6db76c7s",
"01jmerbnaa9r183ry7a4mpe4v8"
]
}
}
]
}
}
```
# Send a credential (/docs/api-reference/credentials/send-a-credential)
Use this endpoint to send a published credential to a recipient.
You can only send credentials with `status = "issued"`.
Currently, the only supported `deliveryMethod` is `email`.
Certifier will send an email to the recipient using the email template configured for the group (credential template) the credential belongs to.
If you are looking for an all-in-one solution that simultaneously creates, issues, and sends a credential, check the [Create, issue, and send a credential](/docs/api-reference/credentials/create-issue-and-send-a-credential) endpoint.
# List credential interactions (/docs/api-reference/credential-interactions/list-credential-interactions)
Returns a list of credential interactions, optionally filtered by credential ID.
# Create a credential interaction (/docs/api-reference/credential-interactions/create-a-credential-interaction)
Creates a new credential interaction event.
This feature uses the browser's `LocalStorage` to identify the actions made by the recipient of the credential. When the user accesses the page with `?recipient=true` query parameter, the application saves that information in `LocalStorage` and removes the parameter from the URL. Actions taken afterwards in the same browser are recorded as the recipient's, until the `LocalStorage` data is cleared.
If you are distributing the credential links independently and wish to track events as originating from the recipient, be sure to include the `?recipient=true` parameter. Without this parameter, we won't be able to mark this person as the recipient, and their actions will be marked under the `guest` actor category.
This approach may not capture every scenario. For example, if a recipient opens a link with `?recipient=true` on one device, then shares the link without the parameter (or opens it on a different device without the stored flag), those subsequent views will be categorized as `guest`. Similarly, if the user clears their `LocalStorage` or uses an incognito session, the system can no longer confirm that they are the recipient.
# Get a design template (/docs/api-reference/design-templates/get-a-design-template)
Retrieves the details of an existing design template by its ID.
# List design templates (/docs/api-reference/design-templates/list-design-templates)
Returns a list of all design templates (certificates and badges) available in your workspace.
# List credential templates (/docs/api-reference/credential-templates/list-credential-templates)
Use this endpoint to list credential templates (groups).
Groups are being renamed to Credential Templates to better reflect their purpose. This is a **naming update only** — all functionality remains the same.
During the transition, you may still see both terms used interchangeably across the API, app, and existing integrations.
# Create a credential template (/docs/api-reference/credential-templates/create-a-credential-template)
Use this endpoint to create a credential template (group).
Groups are being renamed to Credential Templates to better reflect their purpose. This is a **naming update only** — all functionality remains the same.
During the transition, you may still see both terms used interchangeably across the API, app, and existing integrations.
For backward compatibility, this endpoint still accepts the legacy `certificateDesignId` and `badgeDesignId` request fields for the time being. Those fields are deprecated, intentionally omitted from the request schema below, and may be removed without notice. Use `designIds` for all new integrations.
If you use the legacy request shape, the response will also temporarily include legacy `certificateDesignId` and `badgeDesignId` fields for compatibility.
# Get a credential template (/docs/api-reference/credential-templates/get-a-credential-template)
Use this endpoint to get a specific credential template (group).
Groups are being renamed to Credential Templates to better reflect their purpose. This is a **naming update only** — all functionality remains the same.
During the transition, you may still see both terms used interchangeably across the API, app, and existing integrations.
# Update a credential template (/docs/api-reference/credential-templates/update-a-credential-template)
Updates the specified credential template (group) by setting the values of the parameters passed. Any parameters not provided will be left unchanged.
Groups are being renamed to Credential Templates to better reflect their purpose. This is a **naming update only** — all functionality remains the same.
During the transition, you may still see both terms used interchangeably across the API, app, and existing integrations.
For backward compatibility, this endpoint still accepts the legacy `certificateDesignId` and `badgeDesignId` request fields for the time being. Those fields are deprecated, intentionally omitted from the request schema below, and may be removed without notice. Do not send both `designIds` and the legacy fields in the same request.
If you use the legacy request shape, the response will also temporarily include legacy `certificateDesignId` and `badgeDesignId` fields for compatibility.
# Delete a credential template (/docs/api-reference/credential-templates/delete-a-credential-template)
Permanently deletes a credential template (group). This cannot be undone.
Groups are being renamed to Credential Templates to better reflect their purpose. This is a **naming update only** — all functionality remains the same.
During the transition, you may still see both terms used interchangeably across the API, app, and existing integrations.