Skip to main content

External Models API

The External Models API runs a chat model of an external provider with that model's native input and returns its native output unchanged. Every model has a versioned contract with its input and output JSON Schemas and its per-token price. The maximum charge is reserved on your FlyMyAI balance when you start an operation, and the exact charge for the reported usage is taken once when it finishes.

Route keys name the provider and the model, for example openrouter/meta-llama/llama-3.1-8b-instruct.

Authentication and base URL​

  • Base URL: https://backend.flymy.ai/api/v1/model-proxy/
  • Header: X-API-KEY: <your FlyMyAI API key> (see Authentication). Web sessions can use their bearer token instead.
  • A request without credentials gets 403.

1. Find a model​

GET /catalog/?prefix=<route prefix>&limit=<1..100>&cursor=<next_cursor> lists the models you can run:

{"release": "d2a3f6eb-…", "items": [{
"profile": "openrouter.chat.v1", "operation": "chat.completions",
"route_key": "openrouter/meta-llama/llama-3.1-8b-instruct",
"model_version": "meta-llama/llama-3.1-8b-instruct",
"contract_revision": "91e3f184ebc3003f6fdec0544c1de1b91add6a118d1fd44df00e69473355784d"}],
"next_cursor": null}

A cursor is valid for one hour, for the same user and prefix.

2. Read the contract​

GET /contracts/<contract_revision>/ returns the exact contract:

  • the native input and output JSON Schemas (artifact.schemas);
  • the price (artifact.price);
  • the limits: input and output bytes, and a deadline;
  • execution_constraints.

The revision is a SHA-256 of the contract, so a newer catalog never changes an operation you already started.

3. Start an operation​

POST /operations/ with an Idempotency-Key header (your unique key for this exact request) and the native input:

POST /api/v1/model-proxy/operations/
X-API-KEY: <key>
Idempotency-Key: order-42-summary
Content-Type: application/json

{"contract_revision": "91e3f184…",
"inputs": {"model": "meta-llama/llama-3.1-8b-instruct",
"messages": [{"role": "user", "content": "Reply with exactly OK."}],
"max_tokens": 8, "temperature": 0}}

The answer is 202 Accepted with "created": true:

{"operation": {"operation_id": "6fdc75e7-…", "prediction_id": "6fdc75e7-…",
"status": "queued", "stage": "submit",
"contract_revision": "91e3f184…", "cancel_requested": false},
"created": true}

Repeating the same key with the same body returns the same operation (200, "created": false) and never starts a second paid call. The same key with a different body gets 409 idempotency_key_reused.

4. Wait for the result​

Poll GET /operations/<operation_id>/ until status is final. When it is succeeded, output holds the provider's native result:

{"operation": {"operation_id": "6fdc75e7-…", "status": "succeeded", "stage": "submit",
"output": {"id": "gen-…", "object": "chat.completion",
"choices": [{"index": 0, "message": {"role": "assistant", "content": "OK."},
"finish_reason": "stop"}],
"usage": {"prompt_tokens": 32, "completion_tokens": 3, "total_tokens": 35}}}}
StatusMeaning
queuedAdmitted and waiting for a provider slot
runningSent to the provider
succeededFinal; output is set and the exact charge is taken
failedFinal; no charge (for example, the request never reached the provider)
cancelledFinal; cancelled before it was sent, no charge
rejectedFinal; refused before it was sent, no charge
unknownThe provider may have done the work but did not answer clearly. The maximum charge stays reserved until FlyMyAI reconciles it with the provider; the request is never sent twice

5. Cancel​

POST /operations/<operation_id>/cancel/ with the body {}.

  • Before the request was sent: the operation ends cancelled and the reservation is released.
  • After it was sent: the provider offers no proven way to cancel or refund, so cancel_requested becomes true, and the result is still delivered and charged.

Prices and charges​

  • At the start the maximum charge is reserved on your balance: the contract's per-million token rates (kind: "tokens") times its max_usage, the most one call can use.
  • On success the exact charge is taken once: the provider's reported usage at the contract's per-million rates. The rest of the reservation is released.
  • Failed, cancelled and rejected operations release the reservation without a charge.
  • An unknown outcome keeps the reservation until reconciliation.

Errors​

HTTPcodeWhen
400proxy_inputThe input does not match the contract's native schema (nothing reserved, nothing sent)
400invalid_idempotency_key, proxy_contract, proxy_json, proxy_profile_constraint, proxy_prompt_boundMissing or invalid key, missing exact revision, text that cannot be stored, a profile rule (for example stream: true) or a prompt above the contract's priced bound
402proxy_balanceThe balance cannot cover the maximum charge
403- / proxy_permission, proxy_route_staleNo credentials; no access to the model; an inactive account or a contract past its validity
404proxy_contract_missing, proxy_operation_missingUnknown or invisible contract; an operation of another payer
409idempotency_key_reusedSame key, different request
409proxy_route_revoked, proxy_route_disabledThis contract revision is no longer enabled: read the catalog again and use the current revision. Nothing is reserved
413proxy_bytes, proxy_depthRequest body above 264 KiB or the contract's input limit; JSON nested too deep
429proxy_pending_limitToo many unresolved operations for this payer or provider account; retry later
503proxy_receipt_guard and other operational codesTemporary refusal; nothing reserved; retry later

Errors have the shape {"code": "...", "detail": "..."}.

What you can run​

  • Chat (openrouter.chat.v1): the native chat completions input (messages, tools, sampling fields and more) for models with an exact per-token price. No streaming. Each call goes to the model's cheapest available OpenRouter provider that supports every parameter you send, so a provider never silently ignores one, such as max_tokens. You cannot choose the provider.
  • The catalog lists only models whose contract is complete and verified. Streaming, embeddings, image, video and audio models, realtime models and file uploads are not available through this API.

Limits​

  • Request body: 264 KiB. Per contract: limits.max_input_bytes, limits.max_output_bytes and limits.deadline_seconds. An operation not finished by its deadline is not retried with the provider.
  • stream must be absent or false.
  • Provider capacity is shared fairly between payers: queued operations of different payers take turns.