Docs

One endpoint,
every model.

Tare is an OpenAI-compatible gateway in front of every model you use — ours and the ones you bring yourself. One base URL, one key, and every token accounted for.

OperationsTesting failures

Testing failures

The error branches in your own code are the hardest ones to actually exercise: hitting a credit limit for real means spending real money first, and a genuine upstream outage happens on its own schedule, not yours. A mock attaches one of these fixed scenarios to one specific key, so calls made with that key return the simulated response instead of reaching the upstream. No tokens spent, no waiting for a real failure.

Attaching a scenario

Console → API Keys → the flask icon on a key. Or directly:

Code / Terminal
curl -X PUT https://tare.jamerly.ai/v1/mocks/{apiKeyId} \
  -H "Authorization: Bearer $TARE_KEY" -H "Content-Type: application/json" \
  -d '{"scenario":"UPSTREAM_TIMEOUT","enabled":true}'

⚠️ Scoped to the key you name, not your account. Sharing your management credential means it can only touch mocks on keys it owns. That's enough to guarantee testing on a spare key never turns a colleague's production traffic into fake data.

The scenario catalogue

GET /v1/mocks/scenarios returns the list — read it from there rather than hard-coding one, since new scenarios can be added:

ScenarioSimulates
KEY_CREDIT_EXHAUSTEDThis key's own spend cap reached
BUDGET_EXHAUSTEDAccount monthly budget reached
ACCOUNT_BALANCE_INSUFFICIENTAccount balance exhausted
RATE_LIMITEDToo many requests
UPSTREAM_TIMEOUTThe upstream never responds
UPSTREAM_ERRORThe upstream returns a 5xx
INVALID_KEYCredential rejected
MODEL_NOT_FOUNDThe requested model does not exist
BAD_REQUESTMalformed request
CONTENT_FILTEREDContent policy rejection
SUCCESSA normal response, with a body you control (see below)

Each failure scenario returns the same error shape Tare would use for a genuine occurrence of it. The point is to exercise the real branch in your code, not an approximation of it.

SUCCESS: a fixed response, not a failure

The one non-failure scenario. Set responseBody to the exact JSON you want returned, and optionally delayMs to simulate latency. Useful for testing the happy path against a stable, predictable output instead of a live model's variable one.

Expiry

A mock is temporary by construction: leave expiresAt unset and it defaults to 4 hours from when it was enabled. There is no "forever" option. A mock nobody remembers turning on, left running against a key that quietly became a production key, is a worse outcome than having to re-enable it.

What it does not affect

  • Calls on other keys are unaffected. A mock is a property of the key, not the account.
  • No tokens are spent — a mock never reaches the upstream.
  • Disabling or deleting takes effect on the next call; nothing already in flight is changed.

A mocked call still shows up, and can still be billed

A mock always writes a usage row, tagged MOCKED so it's excluded from your success-rate figures either way (a SUCCESS mock would otherwise inflate them; a failure scenario would deflate them). It goes through the same billing path as a real call: token usage is zero either way, but if the model routes through a channel with a per-call minimum fee, that minimum is still charged. The ordinary balance, key-credit and budget checks run first too, same as a real call — an exhausted key can't use a mock to keep exercising the routing fee for free.

Docs last updated Oct 17, 2026, 23:03 (UTC+8)