Skip to main content
Staffify Docs
Updated
Docs/API Reference/Chat Completions
API ReferenceOpenAI-compatible

Chat Completions

Send messages to the Staffi 1.0 model and get a response back. The endpoint uses the industry-standard chat completions format, so any OpenAI-compatible client library works out of the box. Just point base_url and api_key at Staffi.

The model

Staffi 1.0staffi-1.0

Fast, versatile chat model with vision and tool calling. Ideal for chatbots, agents and content generation. 128k context window.

Input pricing

€0.552 / 1M tokens

Output pricing

€2.208 / 1M tokens

Context

128k

Max output

16,384

Vision

yes

Tools

yes

Quick start

Any OpenAI-compatible client works. Only base_url and api_key change.

curl https://api.staffifyai.com/api/v1/chat/completions \
  -H "Authorization: Bearer sfy_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "staffi-1.0",
    "messages": [
      { "role": "user", "content": "Say hi in one word." }
    ]
  }'
POST/api/v1/chat/completions

Creates a model response for the given chat conversation. The full chat completions request body shape is supported. Any parameter not listed below is forwarded to the model untouched.

FieldTypeRequiredDescription
modelstringrequiredMust be "staffi-1.0". Any other value returns 400 UNSUPPORTED_MODEL.
messagesarrayrequiredChat history. Roles: system, user, assistant, tool. Content can be a string or an array of parts (text + image_url for vision).
streambooleanoptionalIf true, chunks are sent as Server-Sent Events. Usage counts arrive in the final chunk.
temperaturenumberoptional0 to 2, default 1. Higher = more random.
top_pnumberoptional0 to 1, default 1. Nucleus sampling.
max_tokensintegeroptionalCap on output tokens. Used for the credits pre-check (worst case).
max_completion_tokensintegeroptionalAlias for max_tokens. Takes precedence if both are provided.
toolsarrayoptionalStandard function tool definitions (OpenAI-compatible schema).
tool_choicestring | objectoptional"auto" | "none" | { type: "function", function: { name } }
response_formatobjectoptional{ type: "json_object" } or { type: "json_schema", ... } for structured output.
seedintegeroptionalBest-effort deterministic sampling.
stopstring | string[]optionalUp to 4 stop sequences.
nintegeroptionalNumber of completions to generate. Default 1.
logprobsbooleanoptionalReturn log probabilities of the output tokens.
presence_penaltynumberoptional-2 to 2, default 0.
frequency_penaltynumberoptional-2 to 2, default 0.
userstringoptionalEnd-user identifier for abuse monitoring on your side.

Example response (non-stream)

{
  "id": "chatcmpl-9a1B2c3D4e...",
  "object": "chat.completion",
  "created": 1751812345,
  "model": "staffi-1.0",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hi!"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 2,
    "total_tokens": 14
  }
}

Streaming

Set stream: true to receive chunks as they are generated. Frames follow the standard SSE format: data: {...} lines separated by blank lines, ending with data: [DONE]. Token usage is included in the final chunk before [DONE].

curl -N https://api.staffifyai.com/api/v1/chat/completions \
  -H "Authorization: Bearer sfy_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "staffi-1.0",
    "messages": [{ "role": "user", "content": "Count from 1 to 5." }],
    "stream": true
  }'

Vision

Send images as content parts alongside text. Either an HTTPS URL or a base64 data URL.

curl https://api.staffifyai.com/api/v1/chat/completions \
  -H "Authorization: Bearer sfy_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "staffi-1.0",
    "messages": [{
      "role": "user",
      "content": [
        { "type": "text", "text": "What is in this image?" },
        { "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
      ]
    }]
  }'

Function calling (tools)

Define tools in the standard chat completions format. The assistant may reply with tool_calls instead of content. Send the tool result back as a role: "tool" message and call the endpoint again to continue.

curl https://api.staffifyai.com/api/v1/chat/completions \
  -H "Authorization: Bearer sfy_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "staffi-1.0",
    "messages": [{ "role": "user", "content": "What is the weather in Amsterdam?" }],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get current weather for a city",
        "parameters": {
          "type": "object",
          "properties": { "city": { "type": "string" } },
          "required": ["city"]
        }
      }
    }]
  }'

JSON mode & structured output

Force the model to return valid JSON, optionally matching a JSON Schema.

curl https://api.staffifyai.com/api/v1/chat/completions \
  -H "Authorization: Bearer sfy_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "staffi-1.0",
    "messages": [{ "role": "user", "content": "Return the JSON object { \"ok\": true }" }],
    "response_format": { "type": "json_object" }
  }'
GET/api/v1/models

Returns the list of models available on this API. Currently a single entry.

{
  "object": "list",
  "data": [{
    "id": "staffi-1.0",
    "object": "model",
    "owned_by": "staffify",
    "display_name": "Staffi 1.0",
    "context_window": 128000,
    "max_output_tokens": 16384,
    "supports_vision": true,
    "supports_tools": true,
    "supports_json_mode": true,
    "input_cost_per_million_tokens_eur": 0.552,
    "output_cost_per_million_tokens_eur": 2.208
  }]
}

Billing

Every request is deducted from your Staffify credits wallet based on the actual token usage. To prevent concurrent bursts from overspending, we use an atomic reservation model:

  1. Reserve. At request start we atomically reserve the worst-case cost (input tokens plus max output tokens) from your wallet. If your balance cannot cover it, the request fails immediately with 402 INSUFFICIENT_CREDITS. This runs under a row lock, so N concurrent requests cannot all pass the same balance snapshot.
  2. Finalize. When the response completes, the reservation is released and the actual cost is deducted (based on usage.prompt_tokens + usage.completion_tokens). Net wallet delta = actual cost only.
  3. Refund on error. If the upstream call errors or the stream closes without usage counts, the full reservation is released. You are never charged for a request you did not receive tokens for.

For streaming requests, usage arrives in the final chunk and finalization runs fire-and-forget after the stream closes. Enterprise tier is post-pay and skips the reservation step.

Retries with the same Idempotency-Key are billed exactly once, even across streaming attempts. See Idempotency below.

Transactions appear in /api/v1/credits history with these types:

  • ai_prededuction: worst-case reservation, negative delta.
  • ai_prededuction_release: reservation released (positive delta) on finalize or refund.
  • ai_completion: actual cost, negative delta.

Rate limits

Chat completions have their own limits, separate from the standard v1 REST limits. Every response includes X-AI-RateLimit-* headers.

TierRequests / minConcurrentMax input tokens / request
PAYG60532,000
Starter2001564,000
Growth50040128,000
Scale1,00080128,000
EnterpriseUnlimitedUnlimitedUnlimited

Requests that exceed the per-tier input token ceiling get 413 INPUT_TOO_LARGE. Requests that exceed the model allowlist for your tier get 403 MODEL_NOT_IN_PLAN.

The request body itself is capped at 2 MB. Requests larger than that are rejected before authentication with 413 REQUEST_TOO_LARGE.

Idempotency

Chat completions supports the industry-standard Idempotency-Key header. SDK auto-retries on transient 5xx errors are safe: two requests with the same key are billed exactly once and return the same response body.

How to use it

Set the Idempotency-Key request header to a value unique per logical request. A UUID per user action is the standard pattern. Keys are scoped to your API key and cached for 24 hours.

curl https://api.staffifyai.com/api/v1/chat/completions \
  -H "Authorization: Bearer sfy_live_YOUR_KEY" \
  -H "Idempotency-Key: 8a1e6b9f-3c2d-4d5f-9e2b-1a0b2c3d4e5f" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "staffi-1.0",
    "messages": [{ "role": "user", "content": "Say hi." }]
  }'

Behavior

  • First request: the response is cached for 24 hours together with a SHA-256 hash of the request body.
  • Retry with the same key + same body: the cached response is replayed and the response includes Idempotency-Replay: true. No second billing.
  • Retry with the same key + different body: returns 409 IDEMPOTENCY_KEY_CONFLICT. Use a fresh key or match the original body.
  • Streaming (stream: true): SSE responses cannot be replayed. A retry with a key that was previously used on a streaming request returns 409 IDEMPOTENCY_STREAMING_REPLAY. Ledger deduplication still runs, so streaming retries are billed once even without response replay.

Limits

  • Key length: up to 128 characters. Longer keys return 400 IDEMPOTENCY_KEY_TOO_LONG.
  • Volume: up to 10,000 unique keys per API key per 24 hours. Exceeding this returns 429 IDEMPOTENCY_KEY_QUOTA_EXCEEDED.
  • Cache TTL: 24 hours. After that a re-used key runs a fresh request.
  • Header is optional. If omitted, no replay cache is written for that request.

Errors

Error envelope

Every 4xx and 5xx response returns an OpenAI-compatible envelope. This shape is consistent across all failure modes, so client SDK error handlers work without modification.

{
  "error": {
    "message": "Insufficient credits. Estimated worst-case cost €0.0362 exceeds balance €0.0100.",
    "type": "invalid_request_error",
    "code": "INSUFFICIENT_CREDITS",
    "param": null,
    "request_id": "chatreq-8a1e6b9f-3c2d-4d5f-9e2b-1a0b2c3d4e5f"
  }
}
  • type: one of invalid_request_error, authentication_error, permission_error, rate_limit_error, upstream_error, server_error.
  • code: the stable machine-readable identifier. Match on this programmatically, never on message.
  • request_id: unique per attempt. If you supplied an Idempotency-Key, that value is used; otherwise a UUID is synthesized. The same id is echoed in the X-Request-Id response header for log correlation.

Error codes

CodeHTTPTypeDescription
MISSING_API_KEY401authentication_errorNo Authorization or X-Api-Key header provided.
INVALID_API_KEY401authentication_errorAPI key not found, revoked, or malformed.
AUTH_RATE_LIMITED429rate_limit_errorToo many failed authentication attempts from this IP.
READ_ONLY_KEY403permission_errorAPI key has read-only scope and cannot perform write operations.
UNSUPPORTED_MODEL400invalid_request_errormodel must be "staffi-1.0".
MODEL_NOT_IN_PLAN403permission_errorThe requested model is not available on your tier.
INVALID_MESSAGES400invalid_request_errormessages missing or not a non-empty array.
INPUT_TOO_LARGE413invalid_request_errorPrompt exceeds the per-tier input token ceiling.
REQUEST_TOO_LARGE413invalid_request_errorRequest body larger than 2 MB.
INSUFFICIENT_CREDITS402invalid_request_errorWorst-case request cost exceeds wallet balance, or wallet is paused.
WALLET_MISSING402invalid_request_errorNo credits wallet exists for this organization. Contact support.
RATE_LIMIT_EXCEEDED429rate_limit_errorGeneral REST per-minute request limit exceeded for this tier.
AI_RATE_LIMIT_EXCEEDED429rate_limit_errorChat completions per-minute limit exceeded for this tier (stricter than general REST).
AI_CONCURRENT_LIMIT_EXCEEDED429rate_limit_errorToo many simultaneous in-flight requests. Reduce parallelism.
IDEMPOTENCY_KEY_TOO_LONG400invalid_request_errorIdempotency-Key header exceeds 128 characters.
IDEMPOTENCY_KEY_CONFLICT409invalid_request_errorSame key already used with a different request body.
IDEMPOTENCY_STREAMING_REPLAY409invalid_request_errorSame key already used on a streaming request. Streaming responses cannot be replayed.
IDEMPOTENCY_KEY_QUOTA_EXCEEDED429rate_limit_errorExceeded 10,000 unique Idempotency-Keys for this API key in 24 hours.
INVALID_REQUEST400invalid_request_errorUpstream provider rejected the request as invalid.
UPSTREAM_FORBIDDEN403permission_errorUpstream provider refused the request.
UPSTREAM_RATE_LIMIT429rate_limit_errorUpstream provider is throttling. Retry with backoff.
UPSTREAM_UNAVAILABLE502upstream_errorUpstream provider temporarily unavailable. Retry.
UPSTREAM_ERROR502upstream_errorUpstream provider returned an unexpected error.
UPSTREAM_AUTH_FAILURE502server_errorInternal issue reaching the upstream provider. Contact support.
UPSTREAM_NOT_FOUND502upstream_errorUpstream provider returned an unexpected 404.

Note: for security reasons the message field never contains fragments of your request. Full upstream error details are logged server-side against request_id. Contact support with that id to get the full context.

Good to know

  • The response model field always echoes back "staffi-1.0". Internal provider names are never exposed.
  • High-availability failover is built-in. If the primary upstream fails, requests are transparently routed to a fallback provider. Some advanced parameters (response_format, seed, logprobs) are best-effort during a failover event and may be ignored.
  • The base URL is https://api.staffifyai.com/api/v1. When configuring an OpenAI-compatible client, that value goes into the base_url / baseURL field.
Chat Completions (Staffi 1.0) - Staffify API