Serverless Inference
General conventions
- Base URL:
BASE_API_URL - Path:
/bff/v1/serverless-inference/{orgIdentifier}/workspaces/{workspaceIdentifier}/{action} - Method: All endpoints use POST.
- Service-specific headers:
X-Region: hanoi-vn(fixed),X-Portal-Roles: <user role in workspace> - Common headers (apiClient):
X-Org-Id,X-Workspace-Id,X-User-Id - Two call types (both POST):
- Query action (list/get/view): empty body; parameters passed via query string.
- Body action (create/edit/...): parameters passed via JSON body.
- Response:
{ "data": <payload> }. Paginated lists:{ items, page, page_size, total, total_pages }. si.playground.chat(stream),si.tts.generate/si.playground.tts(binary) — see Notes.
Endpoints
Model
| # | Action | Type | Parameters | Purpose |
|---|---|---|---|---|
| 1 | si.model.list | query | page, page_size, keyword, categories[], providers[], tags[], model_types[], model_region[], host, status, public, select, access_level | List models |
| 2 | si.model.get | query | code | Model details |
| 3 | si.model.listoption | query | page, page_size | Filter options (category, provider, type, host, data residency) |
| 4 | si.model.codesample | body | { code, sdk? } | Code sample by model |
| 5 | si.model.reqdedicated | body | { name, model_name, email, phone_number, use_case, created_at } | Submit dedicated model request |
API key
| # | Action | Type | Parameters | Purpose |
|---|---|---|---|---|
| 6 | si.apikey.list | query | page, page_size | List API keys |
| 7 | si.apikey.create | body | { name, model_ids[], expires_at?, rate_limit_per_hour? } | Create API key |
| 8 | si.apikey.edit | body | { id, name?, model_ids?, rate_limit_per_hour?, status? } | Edit API key |
| 9 | si.apikey.revoke | body | { id, status? } | Revoke API key |
Usage
| # | Action | Type | Parameters | Purpose |
|---|---|---|---|---|
| 10 | si.usage.view | query | from_date, to_date, model, filter_email, key_name | Usage overview |
| 11 | si.usage.model | query | same as above + page, page_size | Usage by model |
| 12 | si.usage.apikey | query | same as above + page, page_size | Usage by API key |
| 13 | si.usage.options | query | — | Filter options (api_keys, models) |
Playground
| # | Action | Type | Parameters | Purpose |
|---|---|---|---|---|
| 14 | si.playground.options | query | — | Model list for playground |
| 15 | si.playground.chat | body | ISiInferencePayload | Chat completion (stream true/false) |
| 16 | si.playground.embedding | body | { model, input[], dimensions, encoding_format, input_text_truncate, input_type } | Embedding |
| 17 | si.playground.tts | body | { model, input, voice, speed, response_format } | Text-to-speech (binary) |
Voice Studio
| # | Action | Type | Parameters | Purpose |
|---|---|---|---|---|
| 18 | si.tts.generate | body | { model, input, voice, speed, response_format } | Generate speech (binary) |
| 19 | si.voice.history.list | query | page, page_size, model_code, search | Voice history |
| 20 | si.voice.history.delete | body | { id } | Delete one history entry |
| 21 | si.voice.history.deleteall | body | — | Delete all history |
| 22 | si.voice.history.audiourl | body | { id } | Get audio URL (presigned) |
| 23 | si.voice.active | body | { status: boolean } | Enable/disable voice |
Quota (different BFF paths)
| # | Method | Path | Parameters | Purpose |
|---|---|---|---|---|
| 24 | GET | /bff/v1/iam/{org}/workspaces/{ws}/quota-members | page, limit, search, sortBy, order, serviceCode, metricKey (FE defaults: serviceCode=serverless-inference, metricKey=credit) | Member list with quota |
| 25 | GET | /bff/v1/iam/{org}/workspaces/{ws}/quota-members/overview | — | Workspace quota overview |
| 26 | GET | /bff/v1/iam/{org}/workspaces/{ws}/quota-members/usage | — | Current user's usage/quota |
| 27 | POST | /bff/v1/eq/{org}/workspaces/{ws}/quotas | Array of ISiSetQuotaCapPayload | Set quota cap |
| 28 | PUT | /bff/v1/eq/{org}/workspaces/{ws}/quotas | Array of ISiUpdateQuotaCapPayload | Update quota cap |
| 29 | POST | /bff/v1/eq/{org}/quotas:checkUpsert | Array of ISiCheckUpsertQuotaPayload | Validate before setting quota |
Request body
15. si.playground.chat
{
"model": "model-code",
"messages": [{ "role": "user", "content": "Hello", "reasoning_content": null, "name": "", "tool_call_id": "", "tool_calls": [] }],
"stream": true,
"stream_options": { "include_usage": true },
"temperature": 0.7, "top_p": 1, "max_tokens": 1024, "max_completion_tokens": 1024,
"frequency_penalty": 0, "presence_penalty": 0, "n": 1, "seed": 0, "stop": [],
"logit_bias": {}, "logprobs": false, "top_logprobs": 0,
"response_format": { "type": "text" },
"tools": [{ "type": "function", "function": { "name": "", "description": "", "parameters": {} } }],
"tool_choice": null, "metadata": {}, "user": ""
}
content is a string or array of objects (multimodal, e.g. image). Only model and messages are required.
27 / 28 / 29. Quota
// 27 setQuotaCap
[{ "metric_key": "credit", "dimensions": { "service_code": "serverless-inference", "user_id": "..." }, "limit_value": 100, "period": "MONTH" }]
// 28 updateQuotaCap (limit_value null = remove cap)
[{ "metric_key": "credit", "dimensions": { "service_code": "...", "user_id": "..." }, "limit_value": 200, "workspace_id": "..." }]
// 29 checkUpsert
[{ "metric_key": "credit", "dimensions": { "service_code": "...", "user_id": "..." }, "limit_value": 200, "workspace_id": "..." }]
Responses
| Action | Response (data) |
|---|---|
si.model.list | { items: Model[], page, page_size, total, total_pages }; Model: id, code, name, version, model_type, status, access_level, host{id, code, name, host_type(resell|self_hosted), region_name, region_code}, tags[], introduction{short, long, usecase}, capabilities{context_length, max_output_tokens, supports_stream, supports_function_call, supports_vision, supports_json_schema}, docs{overview_md, details_md, benchmarks_md, usage_notes_md, image_url}, pricing{currency, input_price_per_1m, output_price_per_1m, cached_price_per_1m, billing_unit}, max_requests_per_minute, max_requests_per_minute_per_api_key, max_tokens_per_minute, max_tokens_per_minute_per_api_key, has_access_to_non_public_model, category, provider, parameters, performance, created_at, updated_at |
si.model.get | Model (same as above) |
si.model.listoption | { categories[], providers[], model_types[{id, name, code}], hosts[{id, name, count}], data_residency[{id, name}] } |
si.model.codesample | [{ id, model_type, type, sdk, title, description, code_block, response_block, sort_order }] |
si.apikey.list | { items: ApiKey[], page, page_size, total, total_pages }; ApiKey: id, name, masked_key, status, organization_id, user_id, models[{id, name, version, model_type, status}], rate_limit_per_hour, expires_at, last_used_at, created_at, updated_at |
si.apikey.create | { api_key, key_info: ApiKey } (api_key is secret, shown only once) |
si.apikey.edit / revoke | ApiKey |
si.usage.view | { currency, estimated_cost, estimated_cost_change_pct, tokens_in, tokens_in_change_pct, tokens_out, tokens_out_change_pct, total_requests, total_requests_change_pct } |
si.usage.model | { items: [{ model_code, model_name, provider_name, requests, tokens_in, tokens_out, cached_read_tokens, cost, currency }], page, page_size, total, total_pages } |
si.usage.apikey | { items: [{ api_key_name, masked_key, scope, status, user_email, requests, tokens_in, tokens_out, cached_read_tokens, cost, currency }], ... } |
si.usage.options | { api_keys: [{ id, masked_key, name, status, user_email }], models: [{ id, code, name }] } |
si.playground.options | { models: [{ id, code, name, category, voices[], response_format[], price }] } |
si.playground.chat (non-streaming) | { id, object, created, model, provider, system_fingerprint, choices[{ index, finish_reason, logprobs, message }], usage{prompt_tokens, completion_tokens, total_tokens} } |
si.playground.embedding | [{ object, index, embedding: number[] | base64 string }] |
si.voice.history.list | { items: [{ id, model_id, voice, input_text, speed, audio_format, audio_size_bytes, duration_seconds, status, has_audio, audio_url, audio_url_expires_at, expired_at, created_at }], total, page, page_size, total_pages } |
si.voice.history.audiourl | { audio_url, expires_at, ttl_seconds } |
quota-members | [{ id, username, email, firstName, lastName, role, joinedAt, isActive, lastLogin, totalWorkspaces, status, canRemove, monthlyCap, hasCap, mtdUsage, percentageUsed, nearLimit }] |
quota-members/overview | { workspaceQuota{ quotaId, limitValue, period, region, metricKey } | null, summary{ totalMembers, withCap, noCap, nearLimit } } |
quota-members/usage | { monthlyCap, hasOwnCap, mtdUsage, percentageUsed, nearLimit } |
Enums
- API key status:
active,suspended,revoked,blocked,expired - host_type:
resell,self_hosted - response_format (voice):
mp3,wav,opus,flac,caf - encoding_format (embedding):
float,base64; input_text_truncate:none,start,end; input_type:passage,query - Playground category:
large_language_model,vision_language_model,embedding_model,speech_to_text,text_to_speech,rerank,vision_language_action
Notes
- Stream chat:
si.playground.chatwithstream: truesendsAccept: text/event-streamand returns SSE (chunkschat.completion.chunk:choices[].delta{role, content, reasoning_content}; the final chunk may include usage). Errors during streaming return JSON body{ code, message }. - Binary:
si.tts.generate,si.playground.ttsreturn audio files; the audio URL (if any) is in response headerx-audio-url. - Voice Studio max input length is 5000 characters (FE).
- Dedicated inference API docs:
docs/dedicated-inference/api-endpoints.md.
Error codes
Error format
On error, HTTP status ≠ 2xx and the body has the following shape:
{ "code": "SI-KEYS-00004", "message": "...", "params": { "field": "name" }, "role_based": false, "statusCode": 400 }
role_based: true— the error message depends on the caller's role (MANAGEMENT/WS_ADMIN/MEMBER) andparams.quotaLevel.- HTTP 401 — the client logs out.
Common error codes (billing / quota / gateway)
| Code | Meaning |
|---|---|
INSUFFICIENT_BALANCE | Insufficient credits (role-based). Role MANAGEMENT auto-opens the top-up modal. |
INSUFFICIENT_PERMISSIONS | The caller does not have permission to perform this action. |
EQ-RESOURCE-00001 | Quota reached (role-based, per params.quotaLevel: Organization / Workspace / Service). |
EQ-RESOURCE-SYSTEM-LIMIT | Resource quota reached (params.name). |
BFF-CREATING-CONCURRENCY | Another resource is being created in this workspace/service. |
route_not_found | Endpoint does not exist. |
method_not_allowed | Wrong method. Only POST is accepted. |
request_too_large | Body exceeds 64 KB. |
invalid_token | Token is invalid or expired. |
gateway_internal_error | Internal gateway error. |
upstream_unavailable | Backend service is temporarily unavailable. |
gateway_timeout | Backend service timed out. |
Serverless Inference error codes (SI-*)
| Code | Message |
|---|---|
SI-AUTH-00001 | Authorization is required. |
SI-AUTH-00002 | The access token is invalid or has expired. |
SI-AUTH-00003 | User not authenticated. |
SI-AUTH-00004 | Cannot access other user's data. |
SI-AUTH-00005 | Organization scope missing from access token. |
SI-AUTH-00006 | Authentication is temporarily unavailable. |
SI-AUTH-00007 | Invalid or expired token. |
SI-AUTH-00008 | Unauthorized - User not authenticated. |
SI-KEYS-00001 | You are not a member of this organization. |
SI-KEYS-00002 | One or more model IDs not found. |
SI-KEYS-00003 | Cannot create API key: organization balance is insufficient. Please top up and try again. |
SI-KEYS-00004 | An API key with this name already exists in this workspace. |
SI-KEYS-00005 | Failed to create API key. |
SI-KEYS-00006 | Failed to list API keys. |
SI-KEYS-00007 | API key not found. |
SI-KEYS-00008 | You are not authorized to access this API key. |
SI-KEYS-00009 | API key has been revoked. |
SI-KEYS-00010 | API key is inactive. |
SI-KEYS-00011 | Failed to get API key. |
SI-KEYS-00012 | You are not authorized to update this API key. |
SI-KEYS-00014 | Invalid status transition. Revoked and blocked keys cannot be reactivated. |
SI-KEYS-00016 | Failed to update API key. |
SI-KEYS-00017 | You are not authorized to delete this API key. |
SI-KEYS-00018 | Failed to delete API key. |
SI-KEYS-00019 | You are not authorized to revoke this API key. |
SI-KEYS-00020 | Failed to revoke API key. |
SI-LEADS-00001 | A request for this model was already submitted with this email recently. |
SI-LEADS-00002 | Validation failed. |
SI-LEADS-00003 | Failed to submit contact-us lead. |
SI-MODEL_HOST-00001 | Failed to create model host. |
SI-MODEL_HOST-00002 | Failed to get model host. |
SI-MODEL_HOST-00003 | Failed to list model hosts. |
SI-MODEL_HOST-00004 | Failed to update model host. |
SI-MODEL_HOST-00005 | Failed to delete model host. |
SI-MODEL_HOST-00006 | Model host cannot be deleted while models still reference it. |
SI-MODEL_SAMPLES-00001 | Failed to list samples. |
SI-MODEL_SAMPLES-00002 | Failed to get sample. |
SI-PERMISSION-00001 | Forbidden - Insufficient permissions. |
SI-PLAYGROUND-00001 | Failed to list playground model options. |
SI-PORTAL-MODELS-00001 | Failed to create model. |
SI-PORTAL-MODELS-00002 | Failed to get model. |
SI-PORTAL-MODELS-00003 | Failed to list models. |
SI-PORTAL-MODELS-00006 | Failed to get related models. |
SI-PORTAL-MODELS-00007 | Failed to update model. |
SI-PORTAL-MODELS-00008 | Failed to delete model. |
SI-PORTAL-MODELS-00009 | Failed to get capabilities. |
SI-PORTAL-MODELS-00010 | Failed to update capabilities. |
SI-PORTAL-MODELS-00011 | Failed to get pricing. |
SI-PORTAL-MODELS-00012 | Failed to update pricing. |
SI-PORTAL-MODELS-00013 | Failed to get limits. |
SI-PORTAL-MODELS-00014 | Failed to update limits. |
SI-PORTAL-MODELS-00015 | Failed to get rate limits. |
SI-PORTAL-MODELS-00016 | Failed to update rate limits. |
SI-PORTAL-MODELS-00017 | Failed to get docs. |
SI-PORTAL-MODELS-00018 | Failed to update docs. |
SI-PORTAL-MODELS-00019 | Failed to list benchmarks. |
SI-PORTAL-MODELS-00020 | Failed to create benchmark. |
SI-PORTAL-MODELS-00021 | Failed to delete benchmark. |
SI-PORTAL-MODELS-00022 | Failed to list reviews. |
SI-PORTAL-MODELS-00023 | Failed to get average rating. |
SI-PORTAL-MODELS-00024 | Failed to create review. |
SI-PORTAL-MODELS-00025 | Failed to delete review. |
SI-PORTAL-MODELS-00026 | Failed to list active categories. |
SI-PORTAL-MODELS-00027 | Failed to list active providers. |
SI-PORTAL-MODELS-00028 | Failed to retrieve pricing information. |
SI-PORTAL-MODELS-00030 | Failed to query Grafana. |
SI-PORTAL-MODELS-00031 | Failed to list trending models. |
SI-RESOURCE-00001 | {{resource}} not found. |
SI-SYSTEM-00001 | Internal server error. |
SI-USAGE-MODEL-00001 | Failed to retrieve model usage. |
SI-USAGE-MODEL-00002 | Failed to retrieve usage statistics. |
SI-USAGE-MODEL-00003 | Failed to retrieve usage options. |
SI-USAGE-MODEL-00004 | Failed to retrieve API key usage. |
SI-USER-00001 | User not found. |
SI-VALIDATION-00001 | {{field}} should not exist. |
SI-VALIDATION-00002 | {{field}} is required. |
SI-VALIDATION-00003 | {{field}} must be an integer. |
SI-VALIDATION-00004 | {{field}} must be a number. |
SI-VALIDATION-00005 | {{field}} must be a boolean. |
SI-VALIDATION-00006 | {{field}} must follow {{format}}. |
SI-VALIDATION-00007 | {{field}} must be a valid email address containing an @ followed by a domain, for example name@example.com. |
SI-VALIDATION-00008 | {{field}} must be a UUID. |
SI-VALIDATION-00009 | {{field}} must be at least {{limit}}. |
SI-VALIDATION-00010 | {{field}} must not exceed {{limit}}. |
SI-VALIDATION-00011 | {{field}} must be one of: {{allowed_values}}. |
SI-VALIDATION-00012 | {{field}} must be provided only once. |
SI-VALIDATION-00013 | Request body must be valid JSON. |
SI-VALIDATION-00014 | {{field}} is invalid. |
SI-VALIDATION-00015 | {{field}} must be a string. |
SI-VOICE-00001 | History is not found in caller scope. |
SI-VOICE-00002 | History operation conflicts with current state. |
SI-VOICE-00003 | History query/delete/persistence failure. |
SI-VOICE-00004 | Object storage or presigning unavailable. |