Developer reference

API keys & accounts

Authentication, usage, errors and billing endpoints for your integration.

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/plans

The 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.