Authentication
Every call to the v1 API (and the MCP endpoint) authenticates with a single Bearer token. The public verification endpoints (/api/v1/verify/{certificateId} and /.well-known/jwks.json) require no auth — by design, so any party can verify a Verdacert certificate.
Header format
Standard HTTP Bearer auth. Identical across REST and MCP.
Authorization: Bearer vc_<env>_<base32-token>Send the same header on every call. There is no token-exchange step, no refresh, no session — keys are long-lived and you rotate by minting a new one and revoking the old.
Token format
Token strings are self-describing — you can tell sandbox from live at a glance.
| Token | Meaning |
|---|---|
vc_sandbox_p7q8r… | Sandbox key. Free. Synthetic state machine. No real translation, no Stripe charge. Use during development. |
vc_live_abc123… | Live key. Real orders, real translators, real Stripe charges on submit. |
Both share the same shape: prefix vc_, environment tag (live or sandbox), and a high-entropy base32 body. Tokens are hashed at rest — once minted, you see the raw value once in the portal and we can no longer recover it. Save it somewhere safe.
Environments
Sandbox and live share endpoints. The environment is determined by the token prefix — no hostnames to swap.
| Behavior | Sandbox | Live |
|---|---|---|
| Billing | Never charges | Charges default payment method on submit |
| submit response | Job goes to a wall-clock state machine; no real translators touch it | Job enters the production review queue; documents are ingested into R2 |
| status lifecycle | created → paid → processing → in_review → ready in ~2 min (full) / ~90s (R&C) | Driven by real review workflow + AI pipeline events |
| result artifact | Fixed demo PDF + a real Ed25519 certificate with isSandbox=true claim | Real certified PDF + production certificate |
| verify endpoint | Returns acceptanceGuarantee = null, isSandbox = true | Returns USCIS acceptance guarantee |
| Webhook signing | Same HMAC scheme; identical signature verification path | Same HMAC scheme; identical signature verification path |
| Rate limit | 300 req/min per key | 300 req/min per key |
isSandbox: trueclaim in the payload. Your verification code path doesn't branch for sandbox vs live.Key lifecycle
Mint, use, rotate, revoke. All self-serve from the portal.
Minting
From the agent portal:
- Click Mint key. Choose env (sandbox or live) and an optional label.
- Copy the token. This is the only time the raw value is shown.
- Set spend caps + webhook URL if applicable (see below).
Rotation
Mint a new key, deploy it to your runtime, then revoke the old one. Revoked keys reject all requests with REVOKED_KEY immediately — no grace period — so sequence the swap from your side first.
Revocation
In the portal, click Revoke on the key row. Revocation is irreversible. Pending in-flight requests already in our handler complete; subsequent requests fail with REVOKED_KEY.
Spend caps
Optional per-key safety rails — set a maximum cents/day and/or cents/month. Enforced server-side at submit time against the rolling window. Exceeding the cap returns SPEND_LIMIT_EXCEEDED (HTTP 402) and no order is created. Caps apply to live keys only; sandbox is free by design.
Security checklist
If you'd ask 'is this the right way?' — the answer is in here.
- Server-side only. Keys are bearer credentials. Never expose one in a browser bundle, mobile app binary, or anything else a user can extract. If your end-user needs to interact with us directly, surface
publicVerifyUrlfromgetResultinstead. - Use a secret manager. Vercel Project Environment Variables, Doppler, 1Password Secrets Automation, AWS Secrets Manager — anything but
.envin source control. - Distinct keys per environment.Production, staging, and CI each get their own key so revoking one doesn't take the others down.
- Mint one key per platform tenant if you multi-tenant on top of Verdacert. Per-tenant keys are the unit of revocation and spend-cap.
- Webhook secrets are sensitive too. Each key gets its own webhook signing secret; treat it as a credential (see /docs/webhooks).
- TLS only. All endpoints are HTTPS. Plain HTTP requests are rejected.
- Watch
x-vc-request-id. Every response carries one. Log it alongside your own request id so we can correlate when you ask support to investigate.
Local development setup
Drop-in patterns for the three most common stacks.
Node / TypeScript (.env)
# .env (NEVER commit)
VERDACERT_API_KEY=vc_sandbox_…import "dotenv/config";
import { createVerdacertTools } from "@verdacert/ai-sdk-tools";
const tools = createVerdacertTools({
apiKey: process.env.VERDACERT_API_KEY!,
});Next.js on Vercel
# Local
vercel env pull .env.local
# CI / preview / production
vercel env add VERDACERT_API_KEYServer-side only — never expose the key with NEXT_PUBLIC_. Use Route Handlers or Server Actions on the server and stream the result to the client.
Python
import os, requests
resp = requests.post(
"https://verdacert.com/api/v1/quote",
headers={
"Authorization": f"Bearer {os.environ['VERDACERT_API_KEY']}",
"Content-Type": "application/json",
},
json={
"sourceLanguage": "fa",
"useCase": "uscis",
"pageCount": 2,
"speedTier": "standard",
},
timeout=30,
)
resp.raise_for_status()
print(resp.json())