Account & API keys
POST /api/register
{
"email": "you@example.com",
"name": "My App" // optional
}
201
{
"key": "dk_abc123...",
"tier": "free",
"monthly_limit": 500,
"dashboard_signin": false,
"message": "Store your API key securely. It won't be shown again."
}
One key per email. If you have already registered, this endpoint returns 409. Save the key when it is first shown. The optional password field enables dashboard sign-in; the dashboard_signin response field tells you whether it was enabled. This account password is separate from the two passwords used to encrypt a message.
Authentication
Core encryption, vault and usage requests use a Bearer API key. Registration, health and the public plan list do not require one. Some dashboard routes use a signed-in session instead; check the relevant section.
Authorization: Bearer dk_your_api_key_here
Get your key from /register or via POST /api/register.
GET Check usage
GET /api/usage
200
{
"tier": "free",
"calls_this_month": 42,
"limit": 500,
"remaining": 458,
"resets_at": "2026-06-01T00:00:00.000Z"
}
Errors
All errors return JSON with an error field and an appropriate HTTP status code:
{ "error": "Missing or invalid API key" } // 401
{ "error": "Monthly limit reached" } // 429
{ "error": "Required: message, password1, password2" } // 400
{ "error": "Decryption failed. Check your passwords and control data." } // 400
{ "error": "ciphertext and controlData must be valid hex strings" } // 400
{ "error": "Message too large (max 10MB)" } // 400
{ "error": "Password too long (max 1024 chars)" } // 400
Rate limiting: The API enforces per-key monthly limits based on your plan tier, plus burst rate limiting to prevent abuse. Decoy-engine responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset; on 429, obey Retry-After before retrying.
GET Health NO AUTH
Check if the API is running. No authentication required.
GET /api/health
200
{
"status": "ok",
"db": "ok"
}
GET /api/health
x-admin-token: <admin-token>
200
{
"status": "ok",
"version": "2.3.1",
"uptime": 12345,
"db": "ok",
"process": "deniable-crypto"
}
POST Create checkout session
Generate a Stripe checkout URL for upgrading to a paid plan.
POST /api/billing/checkout
{
"plan": "<id from GET /api/billing/plans>",
"successUrl": "https://deny.sh/docs", // optional
"cancelUrl": "https://deny.sh/pricing" // optional
}
200
{
"url": "https://checkout.stripe.com/c/pay/..."
}
POST Customer portal
Generate a link to the Stripe customer portal for managing subscriptions, invoices, and payment methods.
POST /api/billing/portal
200
{
"url": "https://billing.stripe.com/p/session/..."
}
GET List current billing plans
Read the available billing plans and prices from the service instead of hard-coding the catalogue.
GET /api/billing/plans
# No authentication required.
curl --fail-with-body -sS https://deny.sh/api/billing/plansThe response has a plans array. Pass the chosen id as the checkout plan value; checkout validates eligibility and may reject unavailable plans. Display names and prices belong to this response and the pricing page.
Plans & allowances
Use the pricing page for current plans, prices and included limits. Browser encryption and local SDK operations do not require a hosted API plan.
For your engineers: check usage for the allowance on your key, or use the plan-list endpoint when building a billing flow.
Webhook settings
Configure event delivery in the webhook dashboard. For decoy monitoring, start with the tripwire workflow and the registration and event reference.
Single sign-on
Use the single sign-on dashboard for your account’s SSO configuration. See Enterprise for deployment and contract options.