Use the credential endpoints to award a custom credential from your own systems, for example when someone finishes a program you track elsewhere, and to show which credentials an account issues, which ones a person holds and which are waiting to be claimed. Creating, editing, minting and deploying credentials, and claiming one, stay in the Hive3 app: an award notifies the person, who then claims the credential there.
JavaScript and Python follow the same request pattern as the tabbed examples; the cards that show only curl are trivial to translate.
#Scopes, plan and targeting
| Scope | Allows |
|---|---|
credentials:read | Read credentials an account issues, the key owner's claims, and credentials awarded to the key owner. |
credentials:write | Award custom credentials to people. |
No other scope implies these; keys created before they existed need to be replaced (scopes). API access needs Honey Bee or above (overview).
The list route acts on the key owner's own account unless you add ?account=<account ID or @handle>. The API calls an account that others join a community, so for another account the key also needs communities:read (reads) or communities:write (writes), must include that account when the key is limited to selected accounts, and the key owner must currently be an owner, admin or moderator of it. Routes that take a credential ID use the account that owns the credential and ignore ?account=. See managed accounts.
POST /api/v1/credentials/{credentialId}/awards is limited per awarding account to 30 requests per minute and 500 people per day. The budget is shared with the Hive3 app and counts every handle you send, including people who already hold the award. It sits on top of the account-wide budget (rate limits). Over a limit returns 429 rate_limited with a Retry-After header.
#The credential object
| Field | Type | Notes |
|---|---|---|
id, accountId | string | Credential ID and the account that issues it. |
name, description, imageUrl | string | Display details. |
attachmentType | string | general for a custom credential, or event, course, quest, coordination (a booking) or proof_of_human for credentials earned another way. Only general credentials can be awarded. |
attachmentId | string or null | The event, course, quest or booking the credential belongs to, or null. |
claimAccess | string | open (anyone can claim it) or awarded (only people who were awarded it can claim it). |
maxSupply | string or null | Whole-number cap on claims, or null for no cap. |
deployStatus | string | not_deployed, scheduled, pending, confirmed or failed. |
chainName | string | The network the credential is issued on, for example Base. |
createdAt, updatedAt | string | ISO 8601 timestamps (timestamps). |
#Read credentials an account issues
/api/v1/credentialsScopecredentials:readLists the credentials owned by the account, newest first.
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
account | query | string | No | Account ID or @handle. Defaults to the key owner. |
limit | query | integer | No | 1 to 100, default 25. |
cursor | query | string | No | Opaque; page.nextCursor from the previous page (pagination). |
curl "https://app.hive3.tech/api/v1/credentials?account=@design-guild" \
-H "Authorization: Bearer $HIVE_API_KEY"Returns 200.
{
"data": [{
"id": "credential_123", "accountId": "account_123", "name": "Mentor", "description": "Helped five members ship", "imageUrl": "https://example.com/mentor.png",
"attachmentType": "general", "attachmentId": null, "claimAccess": "awarded", "maxSupply": null, "deployStatus": "confirmed", "chainName": "Base",
"createdAt": "2026-10-27T18:00:00.000Z", "updatedAt": "2026-10-27T18:00:00.000Z"
}],
"page": { "nextCursor": null, "hasMore": false }
}Errors: 403 insufficient_scope (missing credentials:read, or communities:read for another account), 403 forbidden (not in the key's allowlist, or your role does not allow it), 404 not_found (account not found).
/api/v1/credentials/{credentialId}Scopecredentials:readReturns one credential. You can read a credential you manage, or one whose issuing account's activity is visible to you; otherwise the response is 404 not_found (Credential not found). mine and claimable are reserved and never credential IDs.
curl https://app.hive3.tech/api/v1/credentials/credential_123 \
-H "Authorization: Bearer $HIVE_API_KEY"Returns 200 with the credential object shown above.
#Award a credential
/api/v1/credentials/{credentialId}/awardsScopecredentials:writeAwards a custom (general) credential to the accounts with the given handles. Each person who is newly awarded gets a notification and can claim the credential in the app. Event, course, quest, booking and proof of human credentials are earned, not awarded, and return 400.
Handles are matched without case and with or without the leading @; @Ada, ada and ada are the same handle, and duplicates and blank entries are ignored. Someone who already holds the award is skipped: they are not counted in awarded and are not notified again, so repeating a request after a timeout is safe. Handles that match no active account are returned in notFound, in lower case without the @. Accounts that are private, still being set up, scheduled for deletion or deleted are reported in notFound exactly like unknown handles, so the response never shows whether such an account exists.
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
handles | body | array of strings | Yes | 1 to 50 handles. The body can be up to 16 KiB. |
curl -X POST https://app.hive3.tech/api/v1/credentials/credential_123/awards \
-H "Authorization: Bearer $HIVE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"handles":["@ada","grace","nobody"]}'const res = await fetch("https://app.hive3.tech/api/v1/credentials/credential_123/awards", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.HIVE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ handles: ["@ada", "grace", "nobody"] }),
});
const { data } = await res.json();import os, requests
res = requests.post(
"https://app.hive3.tech/api/v1/credentials/credential_123/awards",
headers={"Authorization": "Bearer " + os.environ["HIVE_API_KEY"]},
json={"handles": ["@ada", "grace", "nobody"]},
)
data = res.json()["data"]Returns 200 (an award is not a new resource, so there is no 201). Here ada and grace were newly awarded and nobody matched no account.
{ "data": { "awarded": 2, "notFound": ["nobody"] } }| Status | code | When |
|---|---|---|
| 400 | invalid_request | handles is not an array of strings (handles must be an array of account handles), is empty or lists more than 50 people (handles must list 1-50 people), or the credential is not custom (Only custom credentials can be awarded.). |
| 403 | forbidden | You may not manage this credential (You don't have permission to manage this credential), your role does not allow it, or the account is not in the key's allowlist. |
| 403 | insufficient_scope | The key lacks credentials:write, or communities:write for a credential owned by another account. |
| 404 | not_found | Credential not found. |
| 429 | rate_limited | More than 30 award requests in a minute, or more than 500 people in a day. Wait for Retry-After seconds. |
#Read your own credentials
/api/v1/credentials/mineScopecredentials:readLists the key owner's credential claims, newest first. status is pending, confirmed or failed. It always shows the key owner's own claims and ignores ?account=.
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
limit | query | integer | No | 1 to 100, default 25. |
cursor | query | string | No | Opaque; from the previous page. |
curl "https://app.hive3.tech/api/v1/credentials/mine" \
-H "Authorization: Bearer $HIVE_API_KEY"Returns 200. credential is the credential object shown above.
{
"data": [{
"id": "claim_123", "credentialId": "credential_123", "status": "confirmed", "chainName": "Base", "claimedAt": "2026-10-28T09:00:00.000Z",
"credential": { "id": "credential_123", "accountId": "account_123", "name": "Mentor", "description": "Helped five members ship", "imageUrl": "https://example.com/mentor.png", "attachmentType": "general", "attachmentId": null, "claimAccess": "awarded", "maxSupply": null, "deployStatus": "confirmed", "chainName": "Base", "createdAt": "2026-10-27T18:00:00.000Z", "updatedAt": "2026-10-27T18:00:00.000Z" }
}],
"page": { "nextCursor": null, "hasMore": false }
}/api/v1/credentials/claimableScopecredentials:readLists credentials that were awarded to the key owner and are not claimed yet: the credential object plus awardedAt. Claiming happens in the app. It ignores ?account= and takes the same limit and cursor as mine.
curl "https://app.hive3.tech/api/v1/credentials/claimable?limit=10" \
-H "Authorization: Bearer $HIVE_API_KEY"Returns 200.
{
"data": [{
"id": "credential_123", "accountId": "account_123", "name": "Mentor", "description": "Helped five members ship", "imageUrl": "https://example.com/mentor.png",
"attachmentType": "general", "attachmentId": null, "claimAccess": "awarded", "maxSupply": null, "deployStatus": "confirmed", "chainName": "Base",
"createdAt": "2026-10-27T18:00:00.000Z", "updatedAt": "2026-10-27T18:00:00.000Z", "awardedAt": "2026-10-27T18:30:00.000Z"
}],
"page": { "nextCursor": null, "hasMore": false }
}#Award a credential end to end
Find the credential
Call
GET /api/v1/credentials(add?account=for a community you help run) and pick a credential whoseattachmentTypeisgeneral. Keep itsid.Award it
Call
POST /api/v1/credentials/{credentialId}/awardswith up to 50 handles. Keep the response'snotFoundlist.Handle misses and retries
Fix or drop the handles in
notFoundand send only those again. Sending the full list again is safe: people who already hold the award are skipped and not notified twice.Confirm with the recipient
When the recipient's own key calls
GET /api/v1/credentials/claimable, the credential is listed until they claim it in the app. After that it appears inGET /api/v1/credentials/mine.