Developers / API reference

Docs & help

From authentication to project-context injection, model routing and streaming: everything a request goes through inside DeepCodex, in one place.

https://www.deepcodex.cn/v1

Endpoints

Every route the gateway mounts; the table comes from the running server.
ProtocolMethodURLStatusStreamingTypical use
OpenAI Chat CompletionsPOSThttps://www.deepcodex.cn/v1/chat/completionsSupportedStreamsSDKs / curl / most terminals
OpenAI ResponsesPOSThttps://www.deepcodex.cn/v1/responsesSupportedStreamsCodex CLI
Anthropic MessagesPOSThttps://www.deepcodex.cn/v1/messagesBetaStreamsClaude Code
Anthropic MessagesPOSThttps://www.deepcodex.cn/v1/messages/count_tokensBetaNo streamClaude Code
OpenAI Completions (FIM)POSThttps://www.deepcodex.cn/v1/completionsSupportedStreamsCompletion / FIM
Model listGEThttps://www.deepcodex.cn/v1/modelsSupportedNo streamModel discovery by terminals

Protocol support

PlanOpenAI ChatAnthropic MessagesOpenAI Responses
免费版SupportedNot includedNot included
入门版SupportedNot includedNot included
专业版SupportedNot includedNot included
Pro PlusSupportedSupportedSupported
Pro MaxSupportedSupportedSupported

Authentication

Every request carries an API key; the plaintext is shown once, at creation.

Base URL

https://www.deepcodex.cn/v1
  1. 01OpenAI-compatible routes use Authorization: Bearer <your key>. Subsequent requests are rejected after a key is revoked.
  2. 02The Anthropic Messages bridge reads the x-api-key header, or ANTHROPIC_AUTH_TOKEN when set for Claude Code.
  3. 03Explicit project selection takes precedence: for OpenAI Chat / Responses, metadata.project_id overrides X-DeepCodex-Project. Anthropic Messages uses the header; metadata.user_id is client identity and is not used for project attribution. Otherwise, Codex CLI and Claude Code identify projects by installation identity and exact working directory. A generic SDK without this context remains unattributed and does not inject project memory; there is no automatic fallback to the key binding or account default. Use an owned project UUID or external_ref (created on first use); trim to 1–128 UTF-8 bytes without control characters.
Header example
Authorization: Bearer <your-api-key>
X-DeepCodex-Project: <project-uuid-or-external-ref>
Content-Type: application/json

Models

Equivalent tokens are the single quota unit; each model converts at its ratio.
ModelContextMax outputBest forNotesRatio
deepseek-v4.1-flashDeepSeek V4.1 Flash1M393.2KQuick rewrites, retrieval, batches of small tasksVision · Thinking1×

Examples

Streaming and non-streaming are supported. These generic SDK samples do not select a project. For project attribution and memory, add X-DeepCodex-Project or set metadata.project_id in OpenAI Chat / Responses. The key binding and account default are not automatic fallbacks.
curl
curl --no-buffer https://www.deepcodex.cn/v1/chat/completions \
  -H "Authorization: Bearer $DEEPCODEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-v4.1-flash","stream":true,"messages":[{"role":"user","content":"Make the order callback idempotent"}]}'

Terminal setup

Copy the matching configuration; no code changes in your project.
Supported

Minimum plan: Pro Plus (protocol and API-key model permissions still apply)

Config location: ~/.codex/config.toml

Codex CLI
# ~/.codex/config.toml
model = "deepseek-v4.1-flash"
model_provider = "deepcodex"
web_search = "disabled"

[model_providers.deepcodex]
name = "deepcodex"
base_url = "https://www.deepcodex.cn/v1"
env_key = "DEEPCODEX_API_KEY"
wire_api = "responses"
Set your key and launch
export DEEPCODEX_API_KEY=dck_xxx
codex

Send a short message to check the connection. For a 403 response, check that your plan includes the required protocol. Keep keys out of source control.

Rate limits and quota

Not every 429 carries the same headers. RPM, QPS and concurrency limits include Retry-After; exhausted period or key quotas include X-DeepCodex-Quota-Reset. Window headers appear only where applicable and capped; Remaining is omitted if admission did not count the window. Requests refused at admission consume no token allowance.
DimensionMeaningNotes
ConcurrencyRequests in flight at oncePer plan; burst concurrency is counted separately
RPMRequests per minuteCan be tightened per key
QPSRequests per secondPer account; plans without a ceiling take the platform default, and each key can tighten it
Period capEquivalent tokens per billing periodAn in-app / email alert fires at 80%

Response headers

Retry-After
Seconds to back off before retrying a rate-limited call
X-DeepCodex-RPM-Limit / -Remaining / -Reset
Ceiling, requests left and seconds to reset for the current minute
X-DeepCodex-QPS-Limit / -Remaining / -Reset
Ceiling, requests left and seconds to reset for the current second
X-DeepCodex-Quota-Remaining
Equivalent tokens left in this period
X-DeepCodex-Quota-Reset
Period quota reset timestamp in RFC3339 format, not a delay in seconds

Matching reason_code

concurrency_exceeded
Key or account concurrency limit exceeded; platform/model limits use platform_concurrency_exceeded
rpm_exceeded
Above the per-minute request limit
quota_exceeded
Period equivalent-token quota used up
qps_exceeded
Above the per-second request limit; retry after 1s
key_qps_exceeded
Above this API key's own per-second limit

Error codes

The server catalogue lists reason_code values used in request records; the response body may use another code or preserve an upstream error body. HTTP normally denotes the response status. Once streaming has begun, a later failure cannot replace the HTTP 200 already sent; consult the stream error and final request record. Recorded client cancellation uses 499.
Categoryreason_codeHTTPMeaningWhat to do
Authenticationinvalid_api_key401The key is unknown or malformedRegenerate the key on the API Keys page and update the terminal config
Authenticationapi_key_revoked401The key was revokedThe key was revoked; create a new one on the API Keys page
Authenticationapi_key_expired401The key has passed its expiry dateThe key passed its expiry; create a new one on the API Keys page
Authenticationkey_disabled401The key is disabled; enable it on the keys pageRe-enable the key on the API Keys page or use another key
Authenticationuser_disabled403The account is disabledThe account is disabled; contact support
Authenticationaccount_closed403The account was closed while the request was runningThe account was closed during this request. Its keys can no longer be used; contact support if the closure was unexpected.
Authenticationip_not_allowed403The caller's IP is outside the key's allow-listAdd the caller IP to the key's IP allowlist
Authenticationidentity_required403Identity verification is required before callingFinish identity verification in Settings before calling the API
Planno_active_subscription403No active planSubscribe to a plan on the plan page
Plansubscription_expired403The plan period has endedRenew the plan on the plan page
Plansubscription_not_started403The plan period has not startedWait for the plan period to begin or choose an active plan
Planquote_required422PlanReview GET /api/me/subscription/change-plan/preview and submit its quote_token with a stable request_id. A new unreviewed operation is never charged.
Planinvalid_quote422PlanObtain a new quote for this signed-in account. Do not edit the encrypted quote_token.
Planquote_expired409PlanReview a fresh quote. If payment may already have completed, query the original order before paying again.
Planquote_conflict409PlanSubmit the exact reviewed quote without conflicting plan, period or action fields.
Planquote_already_used409PlanQuery the original purchase; the same quote cannot fund another entitlement change.
Planrefresh_due409PlanYour existing paid service has a new allowance period due. Admission and the renewal worker refresh it without selling another period.
Plancontract_unverified409PlanThe old paid contract cannot be verified for a prorated charge. Keep existing rights and review a new purchase after paid service ends.
Planincompatible_upgrade409PlanSchedule this change after all existing paid service ends; it cannot be priced as an in-period upgrade.
Planprepaid_periods_remaining409PlanKeep the future periods already paid for. Use an eligible allowance pack, wait for the next refresh, or schedule a later change.
Planbase_not_exhausted409PlanA complete new period requires exhausted base allowance and no future prepaid periods. Key limits and reservations do not prove exhaustion.
Planno_pending_action409PlanThere is no future purchase arrangement to cancel. Reload the current subscription state.
Planclient_not_in_plan403The plan does not include this terminalUse a terminal included in the plan or upgrade
Planmodel_not_allowed_by_key403The key's model allow-list excludes this modelRelax the model restriction in the key detail
Planmodel_not_allowed_by_plan403The plan does not include this modelPick a model included in the plan or upgrade
Planthinking_not_in_plan403The plan does not include Playground thinking modeTurn off Playground thinking mode or use a plan that includes it
Planprotocol_not_in_plan403The plan does not include this protocolUse a protocol included in the plan or upgrade
Plancapability_unavailable403This request needs image or hosted-tool capability the plan cannot fundThis request carries an image or a provider-executed tool; upgrade to a plan with an assist allowance, or send it without the image or tool
Requestmodel_required400The request has no modelSet the model field in the request body
Requestmodel_not_found404Unknown model codeUse a model code from GET /v1/models
Requestunsupported_model_protocol400This model cannot be called through this protocolCall the model through the protocol listed in the model catalog
Requestdelivery_approval_not_supported422Publishing runs do not support pre-delivery approvalUse a read-only workflow for manual review, or explicitly disable pre-delivery approval before publishing
Requestinvalid_request_error400The request body failed validationFix the request body as described in the error message
Requestvalidation_failed422Console request parameters failed validationCorrect the field identified in the response message and retry. Invalid search parameters and invalid memory kinds return HTTP 422 with code validation_failed; search keywords must be valid UTF-8 without U+0000 and at most 200 characters.
Requestunsupported_parameter400The request contains unsupported stateful Responses parametersRemove unsupported stateful Responses parameters and use a stateless request with store=false
Requestinvalid_json400The body is not valid JSONSend a valid JSON body with Content-Type: application/json
Requestbody_unreadable400The body could not be readResend the request; check the client's connection and proxy
Requestbody_too_large413The body exceeds the size limitShorten the prompt or split the request
Requestclient_aborted499The caller disconnected before the request completedThe caller disconnected before completion. 499 is the request record status, not a response delivered to the disconnected client; inspect recorded usage before retrying
Requestinvalid_project_id400The project reference format is invalidUse an owned project UUID or an external_ref; after trimming, use 1–128 UTF-8 bytes without control characters. For OpenAI Chat/Responses, metadata.project_id takes precedence over the project header; Anthropic Messages uses X-DeepCodex-Project
Requestinvalid_repository_branch400RequestSend optional branch context of at most 255 bytes without NUL, CR or LF. Use a string for metadata.repository_branch. X-DeepCodex-Branch is trimmed; metadata whitespace is retained. When a non-empty trimmed header is present, any included metadata value must match it exactly. Omit empty branch context.
Requestproject_limit_reached400Too many projects on this accountDelete an unused project or upgrade the plan
Quotaquota_exceeded429Equivalent-token quota for the period is used up; the response carries the reset timeTop up, upgrade the plan or wait for the period reset named by X-DeepCodex-Quota-Reset. A Codex thread goal shows this as usage_limited and stops continuing; resume it with /goal resume once the allowance is back
Quotakey_quota_exceeded429This key's token cap for the period is used upRaise the key's quota in the key detail or use another key. A Codex thread goal shows this as usage_limited; resume it with /goal resume after raising the cap
Quotaprovider_quota_exceeded403 / 429The plan lacks this native provider allowance (403), or its allowance is exhausted (429)For 403, choose a plan with this provider allowance or another included model. For 429, wait for the allowance to reset or switch model; resume a usage_limited goal with /goal resume once allowance is available
Platformprovider_capacity_exhausted429Shared model execution capacity is temporarily busy; retry shortly. This is independent of token allowance.Shared provider capacity is busy, independently of your token allowance. Retry after Retry-After; no paid model call was dispatched for this refusal.
Quotaassist_quota_exceeded429The assistant's share of the allowance is used upWait for the assistant quota to reset or upgrade. A Codex thread goal shows this as usage_limited; resume it with /goal resume once the quota is back
Quotarun_budget_exceeded429The run family has exhausted its token budgetThis run family exhausted its token budget; inspect the run budget before starting another run
Requestrun_finished409This run has finished and its run key no longer accepts requestsThis run has finished; use the key belonging to a new active run
Quotarpm_exceeded429Above the per-minute request limitBack off and retry per Retry-After; X-DeepCodex-RPM-Limit/-Remaining/-Reset show the window
Quotakey_rpm_exceeded429This API key's own per-minute ceiling is fullThis API key has its own per-minute ceiling; raise it in the key detail or spread the load over more keys
Quotaqps_exceeded429Above the per-second request limitSpread requests out and retry per Retry-After (1s); X-DeepCodex-QPS-Limit/-Remaining/-Reset show the window
Quotakey_qps_exceeded429This API key's own per-second ceiling is fullThis API key has its own per-second ceiling; raise it in the key detail or spread the load over more keys
Quotaconcurrency_exceeded429Above the key, account or platform concurrency limitLower concurrency or upgrade; retry per Retry-After (2s). A slot frees when an in-flight call finishes, so there is no window to wait out
Quotaplatform_concurrency_exceeded429The platform is at capacity for this model; retry shortlyRetry with backoff; the platform is at capacity
Upstreamupstream_rate_limited429The upstream provider throttled the call; retry shortlyRetry with backoff; the gateway does not replay this failed request with another credential. A new attempt is a separate request
Upstreamupstream_rejected_request400 / 404–428 / 430–499The upstream provider rejected the request; its message is passed throughThe record uses upstream_rejected_request; the upstream response body and 4xx status are passed through (for example 400, 404 or 422). Inspect that body to correct the request
Upstreamupstream_error502The upstream provider returned an errorRetry; the reason is in the request detail
Upstreamupstream_auth_failed502The platform credential was refused upstreamThe request record uses upstream_auth_failed; the response error.code is upstream_error. Retry with backoff and contact support if it persists
Upstreamupstream_unreachable502The upstream provider could not be reachedCheck the request record, then retry with backoff. The gateway does not replay this failed request with another credential
Upstreamupstream_timeout504The upstream provider did not answer in timeShorten the output or retry
Upstreamupstream_stream_broken502The stream ended earlyCheck request details and recorded usage before retrying; a new request incurs separate usage
Upstreamupstream_response_invalid502The upstream answer could not be parsedRetry; the reason is in the request detail
Upstreamupstream_request_invalid502The translated upstream request could not be builtThe translated upstream request could not be built. The record uses upstream_request_invalid; the response error.code is upstream_error. Contact support if it persists
Platformno_upstream_available503No healthy upstream for this model right nowRetry shortly or pick another model
Platformauth_unavailable503Key lookup is temporarily unavailableRetry shortly; contact support if it persists
Platformidentity_unavailable503Identity lookup is temporarily unavailableRetry shortly; contact support if it persists
Platformadmission_unavailable503Quota accounting is temporarily unavailableRetry shortly; contact support if it persists
Platformmodel_catalog_unavailable503The model catalog is temporarily unavailableRetry shortly; contact support if it persists
Platformupstream_catalog_unavailable503The upstream catalog is temporarily unavailableRetry shortly; contact support if it persists
Requestimage_invalid400The image data or format is invalidCheck the prompt length, size and image format
Requestimage_prompt_invalid400The image description or question is invalid (empty or invisible generation description, over 1800 characters, or a null character U+0000)Provide a generation description with visible text, keep descriptions and questions within 1800 characters, and remove null characters (U+0000). Recognition questions may be left blank.
Requestimage_size_unavailable400The requested image size does not match the current service configurationRefresh the image workbench size settings, then retry with a currently supported size.
Upstreamimage_refused422The image model explicitly declined this recognition requestChange the question or image before trying again; waiting for service recovery will not change this refusal.
Requestimage_too_large413The image exceeds the size limitUpload a smaller image (see image_max_bytes)
Planimages_not_in_plan403The plan does not include the image workbenchUpgrade to a plan that includes the image workbench
Platformimages_unconfigured503Image generation has not been configuredImage generation is not configured yet; contact support
Upstreamupstream_failed502The upstream request failedRetry; the reason is in the request detail
Platformnot_supported501The current client version does not support this operationUpdate the assistant to a newer version
Platformassist_disabled503The assistant feature is currently disabledRetry shortly; contact support if it persists
Platformmemory_query_failed503The project memory query failedRetry shortly; contact support if it persists
Requestinvalid_memory_kind422The memory kind is invalidUse one of fact, pitfall, decision, preference or structure. This log reason is returned by console APIs as HTTP 422 with code validation_failed.
Requestsaved_search_empty400A saved search cannot be emptyEnter a keyword before saving the search condition. Empty conditions return HTTP 400 with code saved_search_empty.
Requestsaved_search_limit409The saved-search limit has been reachedDelete a saved condition before adding another
Requestsaved_search_duplicate409This search condition has already been savedThis condition is already saved; open it from the list

Webhooks

Push platform events to your service: agent runs, task summaries, workflow runs, usage thresholds, and completed or blocked goals.

Where to configure

Console → Settings → Webhooks: enter a public https endpoint, pick the events, and copy the signing secret — it is shown once. Up to 10 endpoints per account; each can be disabled, have its secret rotated, or be sent a test event. Delivery records are kept for 30 days.

Webhook access depends on the capability enabled in the current subscription, not a fixed plan name; without that capability the settings tab shows an upgrade notice.

Request body

Every delivery is a POST with Content-Type application/json. The envelope is always id / type / occurred_at / data; the shape of data depends on the event type.

Request body
{
  "id": "agent_run.completed:0f2c0000-0000-4000-8000-000000000001:done",
  "type": "agent_run.completed",
  "occurred_at": "2026-09-17T08:30:11Z",
  "data": { "run_id": "0f2c0000-0000-4000-8000-000000000001", "status": "done", "tokens": 18422 }
}
EventFires whenMain data fields
agent_run.completedA cloud agent run finishes: done (success), failed, or canceledrun_id, status, summary, tokens, cost_cny_cents, compute_seconds, engine, caller, repo, branch, mode, created_at, finished_at; plus project_id, task_id, tests_passed when known
task.summarizedA conversation is digested into a task summary and cardstask_id, project_id, summary, intent_level, request_count, equiv_tokens, cards[], started_at, ended_at
workflow_run.completedA workflow run finishesworkflow_run_id, workflow_id, workflow_name, status, repo, branch, steps_completed, tokens, cost_cny_cents, created_at, finished_at
usage_thresholdThis period's usage crosses an alert thresholdtitle, message, notified_at, threshold_percent, usage_percent, used_equiv_tokens, total_equiv_tokens, period_end, plan_code
goal.completedA goal completesgoal_id, task_id, status, objective, rounds_done, tokens_used, time_used_seconds, rounds
goal.blockedA goal stops because it is blockedgoal_id, task_id, status, objective, rounds_done, tokens_used, time_used_seconds, blocker, blocked_streak

The list above contains subscribable events; unknown subscription types are rejected, and selecting none subscribes to all of them. A manually sent test event uses webhook.test with endpoint_id and message in data; webhook.test is not a subscription type. Payloads are assembled from an explicit field allow-list.

Verifying the signature

v1 = HMAC-SHA256(endpoint secret, "<timestamp>.<raw body>"). Compute it over the raw, unparsed body and compare in constant time; rejecting a timestamp that is far off also stops replays.

Request headers

X-Deepcodex-Event
Event type, the same value as the body's type
X-Deepcodex-Event-Id
Event id; unique per endpoint — use it for idempotency
X-Deepcodex-Delivery
Delivery record id; unchanged across retries
X-Deepcodex-Attempt
Attempt number, starting at 1
X-Deepcodex-Timestamp
Signing timestamp (Unix seconds)
X-Deepcodex-Signature
Formatted as t=<timestamp>,v1=<hex>

Node.js example

Node.js example
import crypto from "node:crypto";

// X-Deepcodex-Signature: t=<unix seconds>,v1=<hex hmac>
export function verifyDeepcodexWebhook(rawBody, signature, secret) {
  if (typeof signature !== "string") return false;
  const parts = /^t=(\d+),v1=([a-fA-F0-9]{64})$/.exec(signature);
  if (!parts) return false;
  const [, timestamp, digest] = parts;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) >= 300) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  const given = Buffer.from(digest, "hex");
  const want = Buffer.from(expected, "hex");
  return given.length === want.length && crypto.timingSafeEqual(given, want);
}

Delivery and retries

RuleDetail
SuccessAny 2xx counts as delivered. The request times out after 10 seconds and 3xx redirects are not followed, so a signed payload never reaches an address you did not register
BackoffAt most 5 attempts including the initial delivery. The first 4 failures wait 30 seconds, 2 minutes, 10 minutes and 1 hour; the fifth failure stops delivery. Total backoff is 1 hour 12 minutes 30 seconds, plus request time and scheduling delays.
At least onceDelivery is at-least-once: key your handler on X-Deepcodex-Event-Id and ignore an id you have already processed
When it stopsDisabling or deleting an endpoint voids its queued deliveries; an event that fails five times is not retried again, and the delivery record keeps the last response code and error
Endpoint rulesA public https URL, without credentials in it, and never a loopback, private or link-local address