Changes to the DEVUP AI API and DEVUP's developer tools that can affect your integration, newest first.
/v1/video/generations: reference media on models that offer it. reference_images, reference_videos and reference_audios take HTTPS URLs, data URIs or multipart/form-data files, and are numbered per kind in the order sent, so a prompt can say image 1 or video 2. Each model lists the kinds it takes and their limits on its model page. See reference media./v1/video/generations: voice (alias voice_id), voice_language and voice_instructions on models that offer them. On models that speak your text, such as avatar models, prompt is the script the avatar speaks. See voice./v1/video/generations: a request body larger than 10,485,760 bytes (10 MiB), files and data URIs included, returns HTTP 413 payload_too_large and is not charged. For large files, send a URL.model_at_capacity with Retry-After: 5, instead of insufficient_quota with a message about your balance. Errors about your own balance are unchanged: 402 insufficient_balance./v1/video/generations: duration, aspect_ratio, size, generate_audio, fps, camera_motion, seed and negative_prompt on models that offer them. Each model's options and accepted values are listed on its model page. A value or combination the model does not accept returns HTTP 400 invalid_value, and the message names what the model accepts. See video options./v1/video/generations: image to video on models that accept images. image sets the first frame and last_frame_image the last frame, as an HTTPS URL, a data URI or a multipart/form-data file: PNG, JPEG or WebP, up to 10 MB and 4096 pixels on a side. See image to video./v1/video/generations: width and height sent together select the output size on models that list exact sizes, as size does. On other models that offer options they return HTTP 400 unsupported_parameter./v1/video/generations: num_frames is deprecated. Use duration./v1/video/generations: a JSON body that is not an object returns HTTP 400 invalid_request_body and is not charged./v1/video/generations: resolution selects the output resolution on models that offer several. Each model's resolutions are listed on its model page./v1/images/generations: width and height sent without size now set the size of the generated image, as size does.devupai 4.0.0 is available. See the Node.js docs and the migration guide.preserve_audio is not sent.GET /v1/models and GET /v1/models/{model}: a new field supports.pdf_input on every model, true when the model accepts PDF files in chat messages. Existing fields are unchanged. See the models docs.POST /v1/chat/completions: PDF files as file content parts, {"type": "file", "file": {"filename": "document.pdf", "file_data": "data:application/pdf;base64,..."}}, for models whose supports.pdf_input is true. A request can carry several PDF files. See PDF documents.POST /v1/chat/completions: an image sent to a model whose supports.vision is false, or a PDF file sent to a model whose supports.pdf_input is false, returns HTTP 400 unsupported_content (previously invalid_request_error) and is not charged. A file part that is not an inline PDF returns the same code. Code that handles this case through invalid_request_error should match on unsupported_content. See the errors docs.POST /v1/chat/completions: a request body larger than 10,485,760 bytes (10 MiB), Base64-encoded file data included, returns HTTP 413 payload_too_large (previously HTTP 400 invalid_request_body) and is not charged. See request size.budget_exhausted, and the token's spend stays within its limit.scoped_budget_unbounded_request. Send it with an account API key./v1/audio/transcriptions: json and verbose_json responses include _devup with cost_dzd, balance_dzd and request_id, like the other endpoints.url returned by /v1/images/generations, /v1/video/generations and /v1/audio/speech) answer Range requests with HTTP 206 and Content-Range, and send Content-Length, so players can seek in video and audio. A range that starts after the end of the file returns HTTP 416./v1/images/generations: images returned with response_format: "url" keep their original format (PNG, JPEG, WebP or GIF)./v1/images/generations: models priced per image charge for the images included in the response./v1/video/generations: a request that returns no video is not charged./v1/audio/transcriptions: audio with a duration of 0 seconds is charged 0 DZD./v1/video/generations: videos returned with response_format: "url" keep their original format (MP4, QuickTime or WebM)./v1/images/generations: n is an integer from 1 to 10. Any other value returns HTTP 400 invalid_n./v1/images/generations, /v1/video/generations and /v1/audio/transcriptions: a multipart/form-data body that cannot be read returns HTTP 400 invalid_request_body./v1/images/generations and /v1/video/generations: every error response carries type and code next to message. See the errors docs./v1/images/generations: at most n images are returned and charged./v1/images/generations: models priced by image tokens charge output tokens in proportion to the images included in the response./v1/audio/speech: a request whose model response contains no audio returns HTTP 502 DEVUP_COMPUTE_ERROR and is not charged.POST /v1/chat/completions: a request that sends reasoning_effort together with tools to a model that does not accept the two together returns HTTP 400 unsupported_parameter with the message This model does not accept reasoning_effort together with tools on /v1/chat/completions. Set reasoning_effort to "none", remove the tools, or use /v1/responses., with or without streaming. To use tools with such a model, send reasoning_effort none, or send the request to POST /v1/responses with reasoning.effort. See the errors docs.GET /v1/models and GET /v1/models/{model}: max_output_tokens is each model's own output limit, and models have different limits: the largest max_tokens and max_completion_tokens the model accepts on POST /v1/chat/completions, the largest max_output_tokens on POST /v1/responses and the largest max_tokens on POST /anthropic/v1/messages. Read it per model from GET /v1/models. See the models docs.GET /v1/models and GET /v1/models/{model}: max_output_tokens is listed for models that generate text (type text-generation).POST /v1/responses: a max_output_tokens above the model's max_output_tokens returns HTTP 400 invalid_max_tokens (previously invalid_request_error), with the message max_output_tokens must be between 1 and N., where N is the model's limit. A value that is not a whole number of at least 1 returns the same code with the message max_output_tokens must be a whole number between 1 and N. Code that handles this case through invalid_request_error should match on invalid_max_tokens. See the errors docs.POST /v1/responses: a request without max_output_tokens is limited to the model's default output length, which is at most its max_output_tokens, as a request without max_tokens is on POST /v1/chat/completions. Send max_output_tokens, up to the model's limit, to choose the length.GET /v1/models and GET /v1/models/{model}: a new optional field tool_calls on each model. tool_calls.chat_completions is false when the model's tool calls are served on /v1/responses (a POST /v1/chat/completions request with tools then returns HTTP 400 use_responses_api), and true when they are served on /v1/chat/completions. tool_calls.parallel is true when the model can return several tool calls in one reply. Each key is present only when known, and the field is omitted when none is. Existing fields are unchanged. See the models docs.GET /v1/models and GET /v1/models/{model}: reasoning.values lists the reasoning levels you can send to the model with reasoning.parameter, from least to most reasoning. none, when listed, turns reasoning off. How much each level changes the reasoning depends on the model. Some models list a single value, which turns reasoning on. parameter and values can now also appear on models whose reasoning.mode is always, where they set the reasoning level. The shape of reasoning is unchanged. See reasoning controls.model_at_capacity: fewer requests return HTTP 429 model_at_capacity when a model is busy; a reply at such a moment can take a few seconds longer. When the error is returned, its body and its Retry-After: 5 and retry-after-ms: 5000 headers are unchanged, and code that already retries on it needs no change.insufficient_balance: a request with a non-empty tools array that your balance does not cover in full returns HTTP 402 insufficient_balance on POST /v1/chat/completions and POST /v1/responses, and HTTP 402 with the same message on POST /anthropic/v1/messages, so a reply with tool calls is never shortened to fit your balance; code that sends tools should handle this error. On POST /v1/chat/completions and POST /anthropic/v1/messages, a request without tools is still answered with a smaller max_tokens and the notice max_tokens_capped_by_balance, and such requests need less available balance to start. See the errors docs.* in place of each group of those characters. See the files and context docs.+ menu and the @ results, and the tooltip of a file you attach above the message box, so it can be told apart from the same name without them.@ in the message box to find a workspace file by name.AGENTS.md and .devupai/rules.md at each folder root.GET /v1/me returns the profile of the account that owns the API key, with exactly the fields object (account_profile), display_name, initial, email_hint (for example a•••@example.com; the full email is never returned) and avatar (null, or the picture's url and etag). It accepts account API keys (sk-devup-...) in Authorization: Bearer or x-api-key, is sent with Cache-Control: private, max-age=300, is not billed and counts against your account's rate limit.GET /v1/me/avatar returns the account's profile picture as PNG, JPEG or WEBP bytes: square, at most 256×256 pixels and 256 KB. Its ETag equals avatar.etag in GET /v1/me; send it in If-None-Match to receive 304 Not Modified. It is sent with Cache-Control: private, max-age=86400, uses the same authentication and is not billed. While the picture is temporarily unavailable it returns HTTP 503 service_unavailable with a Retry-After header.avatar_not_found: HTTP 404 with the message This account has no profile picture. on GET /v1/me/avatar when the account has no profile picture.scoped_token_not_allowed: HTTP 403 with the message This endpoint accepts account API keys (sk-devup-...) only, not scoped tokens. on GET /v1/me and GET /v1/me/avatar when a scoped token is sent.model_not_found when the requested model ID is not present in the catalog.POST /v1/rerank now consistently returns HTTP 404 model_not_found for unknown or unpublished model IDs.model_not_found now exclusively represents an unknown or unpublished model ID (HTTP 404). A valid model that is temporarily unavailable returns retryable HTTP 503 model_unavailable. Retry guidance may be communicated through the Retry-After and retry-after-ms headers.model_at_capacity with Retry-After: 5 and retry-after-ms: 5000 headers; some of these responses were previously returned as the account error rate_limit_exceeded.POST /anthropic/v1/messages: an error during a stream now always carries the documented error message for its code.response_format json_schema on some models that do not take it directly: requests without streaming now return the JSON in message.content, with the notice structured_output_via_tool. With strict: true, a reply that does not match the schema returns HTTP 400 structured_output_mismatch and is not billed; without it, the JSON is returned with the notice structured_output_not_matched. GET /v1/models shows which of these models support it.GET /v1/models/{model} returns one model: the same object as its entry in GET /v1/models, with the same authentication, Cache-Control and ETag. Send the model ID as-is, for example /v1/models/deepseek-ai/DeepSeek-V4-Pro; the URL-encoded form deepseek-ai%2FDeepSeek-V4-Pro also works. An unknown ID returns HTTP 404 model_not_found.GET /v1/models and GET /v1/models/{model} accept the API key in the x-api-key header as well as in Authorization: Bearer. When both are sent, Authorization is used. Browser requests may send the x-api-key and anthropic-version headers.endpoint_not_found: HTTP 404 with the message No endpoint exists at {METHOD} {path}. for a path under /v1/ that is not an endpoint.model_at_capacity: HTTP 429 when the model is momentarily at capacity, with Retry-After: 5 and retry-after-ms: 5000. The OpenAI and Anthropic SDKs retry it automatically. On POST /anthropic/v1/messages, the same case returns HTTP 529 with an overloaded_error and the same headers.rate_limit_exceeded is returned only when your account's rate limit is reached, with the X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and Retry-After headers. A model at capacity returns model_at_capacity; code that retries on rate_limit_exceeded should also retry on model_at_capacity.max_output_tokens in GET /v1/models is always an integer: the largest max_tokens and max_completion_tokens the model accepts on POST /v1/chat/completions, and the largest max_tokens on POST /anthropic/v1/messages. A larger value returns HTTP 400 invalid_max_tokens with the model's limit in the message, for example max_tokens must be between 1 and 32,768.response_format json_object or json_schema: on models that write JSON inside a Markdown code fence, message.content now holds the JSON alone, with or without streaming, so it parses as JSON. The notice json_fence_removed is returned with these replies.DEVUP_COMPUTE_ERROR, service_unavailable).POST /v1/chat/completions and POST /anthropic/v1/messages: when the model returns an error before any output, the response is now an HTTP error with the same JSON body as without streaming, for example HTTP 400 context_length_exceeded (previously an empty stream with HTTP 200). It is not billed. A stream that ends with an error, or without any output, is not billed; when you stop a stream, the input and the output sent up to that point are billed.POST /v1/chat/completions: an error during a stream is sent as a data: {"error": …} event followed by data: [DONE].context_length_exceeded now has one message for every model, with or without streaming._devup.warnings, each with a stable code, when it is returned, an example message and what to do.POST /v1/chat/completions, POST /v1/responses and POST /anthropic/v1/messages: responses with notices now carry the X-DEVUP-Warnings header, listing the notice codes, on streamed and non-streamed responses. Browsers can read it. _devup.warnings is unchanged.tool_choice_not_enforced: tool_choice is "required" or a named function on a model that chooses its tools itself. reasoning_not_available: reasoning_effort or reasoning is sent to a model whose reasoning is not configurable. GET /v1/models shows which models each one applies to.GET /v1/models: two new optional fields on each model. tool_choice shows, for each known tool_choice value, whether the model enforces it. reasoning shows the model's reasoning mode (always, on_request or none) and, when known, the parameter and values that control it. Each is omitted while unknown. Existing fields are unchanged.GET /v1/models (supports), the model pages and the compare page now show the capabilities each model has in practice. For example, structured output is shown for models that return JSON matching the requested schema. Fields and their meaning are unchanged.error.code and the X-DEVUP-Warnings header instead. The Server response header is now DEVUP-AI.response_format (json_schema or json_object) that the model does not support now returns HTTP 400 response_format_not_supported, with a message that says what to change. It is not billed.tools_not_supported, conversation_must_end_with_user, empty_message_content, feature_not_supported, or invalid_request_error when no more specific code applies. They are not billed. Some of these previously returned HTTP 502; a 502 now always means the request is worth retrying.wrong_endpoint, naming the endpoint that serves it, on /v1/chat/completions, /v1/responses, /v1/embeddings, /v1/images/generations, /v1/audio/speech, /v1/audio/transcriptions and /v1/video/generations. It is not billed. /v1/responses serves text models; embedding and image models are served on their own endpoints.POST /v1/chat/completions: reasoning: {"enabled": true} now turns on reasoning on models that take reasoning_effort, as reasoning_effort: "medium", with a notice. When reasoning_effort is also sent, it applies._devup.warnings.content between <thinking> tags is now returned in reasoning_content, with or without streaming, as <think> blocks already were.Content-Type: application/json; charset=utf-8, so clients that assumed another encoding, such as Windows PowerShell 5.1, show non-ASCII text correctly."".nvidia/Nemotron-3-Nano-30B-A3B and nvidia/NVIDIA-Nemotron-3-Super-120B-A12B: reasoning is streamed in reasoning_content as it arrives.moonshotai/Kimi-K3: the answer streams as it is written.unsupported_parameter error now names the parameter the model does not accept.max_tokens ends a reply while the model is still reasoning, that reasoning is returned in reasoning_content.POST /v1/chat/completions and POST /v1/responses: a tool_choice of "required" or a named function without tools now returns HTTP 400 missing_tools. These requests are not billed. "auto" and "none" without tools are still accepted.POST /v1/chat/completions: tool_choice values "required" and a named function are now honoured on models that support them.GET /v1/models now returns name and context_window (integer), and its responses are private to the caller.chatcmpl- followed by a UUID, for streamed and non-streamed responses.choices array and carries usage. No other chunk carries usage. The data: [DONE] terminal event may not be present in every stream._devup include _devup.request_id, which matches the x-request-id response header./v1 API, including GET /v1/models and GET /v1/user/balance, and from /anthropic/v1/messages now carries an x-request-id header with the request's ID (it starts with req_), on successes and errors alike. For billed requests, your dashboard shows the same ID; quote it when you contact support. To match requests with your own logs, send an x-client-request-id header (1–64 characters of letters, digits, ., _, : or -); it is echoed back in the x-client-request-id response header. An x-request-id header you send is now echoed back as x-client-request-id rather than used as the request ID. See Request IDs.GET /v1/user/balance and _devup.balance_dzd return 2 decimals, and _devup.cost_dzd shows 4.POST /v1/chat/completions and POST /v1/responses: an x-request-id header is accepted only when it is 1–64 characters of letters, digits, ., _, : or -; any other value is replaced by a generated id.completion_tokens, and billed as output, for every model.<think> tags is now returned in reasoning_content, not in content, streaming and non-streaming.unsupported_content, unsupported_parameter, context_length_exceeded or invalid_request_format.POST /v1/chat/completions now checks that the model is served on Chat Completions before processing the request. Models served only on /v1/responses, and tool calls on models whose tool calling is served on /v1/responses, receive HTTP 400 use_responses_api, pointing the request to /v1/responses.GET /v1/models now requires an API key in the Authorization: Bearer header and is limited to 60 requests per minute per account. Requests without a valid key receive HTTP 401. Clients that already send an API key are unaffected.tpm_limit_exceeded with a Retry-After header. See Rate Limits.rate_limit_exceeded, concurrency_limit_exceeded, tpm_limit_exceeded and flex_capacity_unavailable.prompt_cache_options, then read it at the cached input price with prompt_cache_key. Available on models whose page shows a Prompt cache retention table. See Prompt Caching./v1/chat/completions: set "service_tier": "priority" on supported models. See Service Tiers./v1/chat/completions: set "service_tier": "flex" on supported models for a lower price when your workload can wait. See Service Tiers.usage.estimated_cost is no longer returned by /v1/chat/completions, /v1/embeddings, /v1/responses and /v1/inference/{model_path}. Where a response includes _devup, _devup.cost_dzd is the amount charged for the request.usage.prompt_tokens_details.cache_write_tokens is no longer returned. usage.prompt_tokens_details.cached_tokens is unchanged.choices[].message.name is no longer returned when its value is null.data[0].url, the shape the API returns. If you generated an SDK from an earlier version of the specification that exposed video_url, regenerate it and read data[0].url. Clients that already read data[0].url need no change.missing_api_key, invalid_api_key, token_revoked, insufficient_balance, budget_exhausted, account_suspended and model_not_allowed. token_revoked, budget_exhausted and model_not_allowed apply to scoped tokens only.POST /v1/inference/{model_path} now documents its 503 response.