Skip to main content
Use this page before you point real traffic at OpenType, and again when you review an integration. Each item says what to do, why it matters, and where the details are. The code in Code examples implements most of it.

Keys and secrets

  • One key per environment and per service. Development, staging and production each get their own key, named after the process that holds it. Rotating or revoking one then touches nothing else. See Key rotation.
  • The smallest scope set on each key. A service that sends runs needs runs_write and runs_read. A dashboard needs usage_read and billing_read. Only the tool that manages keys needs keys_read and keys_write. Scopes are fixed when a key is created. See Scopes and roles.
  • The secret lives in a secret store, not in source code, container images, build logs or client-side code. Load it into OPENTYPE_API_KEY at runtime. The secret is shown once, at creation or rotation, and cannot be recovered. See API key security.
  • Calls come from your servers, not from browsers or mobile apps. Anyone who can read a secret can spend your credit.
  • A key id, never a secret, goes in a URL. A secret in a key route’s path is refused with 400 secret_in_path; treat that secret as leaked and rotate it.
  • A rotation plan exists. Keys never expire. Decide how often you rotate, who does it, and how you roll out a new secret without downtime. See Key rotation.

Requests

  • Every POST /v1/runs carries an Idempotency-Key derived from your business id, such as ticket-4822-triage, not a random value per attempt. A key is 1 to 255 bytes of visible header text, unique per organization, and never expires. See Idempotency.
  • A changed body gets a new key. Reusing a key with a different body is refused with 409 idempotency_conflict. Append an attempt suffix, such as ticket-4822-triage-r1.
  • deadline_ms is set on purpose. For a decision run it defaults to 30,000 plus 120,000 per 262,144 input tokens and is clamped to 1 to 150,000. A run that misses it fails with 504 deadline_exceeded.
  • Your HTTP client timeout is longer than deadline_ms. POST /v1/runs answers only when the run has settled. Use at least 160 seconds, 10 seconds over the longest deadline; the official SDKs default to 170 seconds.
  • A client timeout is recovered by resending the same request with the same key. You get the stored run back instead of a new one: 200 with replayed: true once it has settled, 202 while it has not. A replay is never charged. See Polling.
  • max_output_tokens is close to what the answers need. It is required, and counts in full against a token quota at admission.
  • Decision inputs stay under 262,144 tokens. The estimate is the bytes of state divided by 4, rounded up, plus the bytes of the question set (instructions, questions, draws and think_tokens) divided by 4, rounded up. Above 262,144, the run is refused with 413 input_too_large. See Models and pricing.
  • Bodies stay under 4 MiB. Larger bodies are refused with 413 body_too_large.
  • The model is set on purpose. Decision runs take an optional model: "neon-1.1" or "neon-latest". Any other value is refused with 400 unknown_model.

Observability

  • You send your own x-request-id on every call and log it next to your business id. A value of 1 to 128 characters from A-Z a-z 0-9 . _ - is echoed back; anything else is replaced by a server id. See Request ids.
  • You log the x-request-id response header and error.request_id for every failure. It is the value to quote when you ask about a request.
  • You log run_id, cost_micros and usage from each run, so your records join the usage ledger.
  • You branch on error.code, never on message. Messages are for people and can change. See Error handling.
  • You handle bodies that are not JSON. A few framework refusals on non-run routes (malformed JSON, a wrong Content-Type, an unknown field or query parameter) return plain text with no request_id in the body. Fall back to the status and the x-request-id header.

Error handling

  • Retries are bounded: a few attempts with exponential backoff and jitter, then an alert.
  • The policy comes from the status and error.code, as in the table above. See Error handling.

Credit and spend

  • Auto-recharge is on, with a card saved, a threshold above one hour of spend plus 20,000 micros, and an amount above your busiest hour. It charges at most once per organization per clock hour. See Auto-recharge.
  • The balance covers your concurrency. Every run in flight holds its spend ceiling, up to 20,000 micros ($0.02), until it settles.
  • A balance alert exists, fed by GET /v1/billing, that fires before balance_micros gets near zero.
  • A quota alert exists, fed by GET /v1/quota, that fires when remaining_spend_micros or remaining_tokens falls below one normal day’s use.
  • Someone reviews spend with GET /v1/usage/daily or the console Usage page.
  • Billing permissions are in the right hands. Only owners, admins and billing users hold billing_write.

Before the switch

Run these once from the production environment. OPENTYPE_API_KEY is the production runtime key; OPENTYPE_MONITOR_KEY is a separate key with usage_read and billing_read for your monitoring.
Expect a completed run with a small cost_micros, room left in the quota (or null limits), a positive balance, and auto-recharge enabled with a saved card.