All errors return a JSON body with an error field:
"error": "description of what went wrong"
The HTTP status code indicates the error category. The error message
provides a human-readable description.
| Code | Name | When it’s returned |
|---|
200 | OK | Request succeeded |
400 | Bad Request | Invalid input — missing required fields, invalid format, unsupported value |
401 | Unauthorized | Missing, invalid, or expired API key / session token |
403 | Forbidden | Your role lacks the required permission for this endpoint |
404 | Not Found | Resource doesn’t exist, doesn’t belong to your tenant, or was already revoked |
409 | Conflict | Duplicate — email already registered, tenant slug taken, domain already claimed |
500 | Internal Error | Server-side error — retry or contact support |
| Error | Cause | Fix |
|---|
unauthorized | Missing or invalid Authorization header | Include Authorization: Bearer <token> with a valid API key or session token |
unauthorized (after working) | Session or API key expired | Re-login or create a new API key |
| Error | Cause | Fix |
|---|
role 'viewer' lacks permission: credentials:issue | Your role doesn’t have the required permission | Ask a tenant admin to upgrade your role, or use a different API key |
API key missing scope: dids:create | API key was created with restricted scopes | Create a new API key with the needed scope, or one with no scope restrictions |
| Error | Cause | Fix |
|---|
monthly credential issuance limit reached (100) | Plan’s monthly credential quota exhausted | Upgrade your plan or wait for the next billing month |
DID limit reached (1) | Maximum DIDs for your plan | Upgrade your plan or deactivate unused DIDs |
tenant member limit reached (1) | Maximum team members for your plan | Upgrade your plan |
| Error | Cause | Fix |
|---|
DID did:key:z6Mk... not found in tenant | The DID doesn’t belong to your tenant | Use a DID you created in your own tenant |
issuer DID is deactivated | Trying to issue with a deactivated DID | Create a new DID |
credential not found or already revoked | Credential doesn’t exist or was already revoked | Check the credential ID |
invalid role_id | Role UUID doesn’t exist | Use GET /v1/roles to list valid role IDs |
email already registered | Email taken by another user | Use a different email or login with the existing account |
tenant slug already taken | Another tenant uses this slug | Choose a different slug |
| Error | Cause | Fix |
|---|
email required, password must be >= 8 chars | Registration validation failed | Provide a valid email and password of 8+ characters |
score must be 0-100 | Trust attestation score out of range | Use an integer between 0 and 100 |
weight must be 0.0-1.0 | Trust attestation weight out of range | Use a decimal between 0.0 and 1.0 |
invalid assurance level | Unrecognized level string | Use: low, substantial, or high |
unsupported format | Credential format not recognized | Use jwt_vc_json or vc+sd-jwt |
Rate limits are applied per tenant, per minute. When exceeded, the API
returns 429 Too Many Requests.
| Plan | Requests/minute | Burst |
|---|
| Developer (free) | 10 | 20 |
| Startup ($49) | 100 | 200 |
| Business ($199) | 1,000 | 2,000 |
| Enterprise | 10,000 | 20,000 |
const result = await fetch('/v1/credentials/issue', { ... });
if (result.status === 429) {
const retryAfter = result.headers.get('Retry-After');
await sleep(parseInt(retryAfter || '60') * 1000);
| Resource | Developer | Startup | Business | Enterprise |
|---|
| Credentials/month | 100 | 1,000 | 10,000 | Unlimited |
| DIDs | 1 | 5 | 25 | Unlimited |
| Team members | 1 | 5 | 25 | Unlimited |
| Custom roles | No | No | Yes | Yes |
| Custom domains | No | No | Yes | Yes |
| API keys | Unlimited | Unlimited | Unlimited | Unlimited |
| Webhooks | Unlimited | Unlimited | Unlimited | Unlimited |
| Compliance frameworks | All 7 | All 7 | All 7 | All 7 |
| Trust API | Yes | Yes | Yes | Yes |
| DIDComm | Yes | Yes | Yes | Yes |
Check current usage and remaining quota:
curl https://api.baseid.cloud/v1/admin/usage \
-H "Authorization: Bearer bsk_live_..."
Upgrade your plan:
curl -X POST https://api.baseid.cloud/v1/admin/billing/upgrade \
-H "Authorization: Bearer bsk_live_..." \
-H "Content-Type: application/json" \
All API responses include security headers:
| Header | Value | Purpose |
|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains | Enforce HTTPS |
X-Content-Type-Options | nosniff | Prevent MIME sniffing |
X-Frame-Options | DENY | Prevent clickjacking |
X-XSS-Protection | 1; mode=block | Enable browser XSS filter |
Referrer-Policy | strict-origin-when-cross-origin | Limit referrer leakage |
Permissions-Policy | camera=(), microphone=(), geolocation=() | Disable unnecessary browser features |
- Check the error message — BaseID returns specific error text, not generic codes
- Verify your API key — ensure it starts with
bsk_live_ and hasn’t been revoked
- Check permissions — use
GET /v1/auth/me to see your current role and permissions
- Check quotas — use
GET /v1/admin/usage to see remaining capacity
- Use the Playground — the console’s API Playground lets you test requests interactively