Työäly Gate · API reference
One request before an action. One decision back.
The decision layer for AI agents. An agent sends what it intends to do, or the text and data it
has about it, and Gate answers allow, review, deny or more_information with a confidence,
reason codes, risk scores, what is missing, and an audit id. Gate never executes anything.
The machine-readable contract is docs/openapi.json (OpenAPI 3.1, generated from the code by
python -m gate.cli openapi). Everything under /v1 only gains fields: a client written today keeps
working.
Base URL and authentication
https://api.tyoaly.com
Authorization: Bearer tyo_test_… # decides on your test environment
Authorization: Bearer tyo_live_… # decides on your live environment
A key belongs to one organisation and one environment. Keys are shown once, when created, in the
console or by Työäly; a revoked key answers 401 from the next request on.
One decision: POST /v1/gate/evaluate
The version-1 request: the action, who proposes it, and the context the agent has.
{
"action": {"type": "refund", "amount": 649, "currency": "EUR", "target": "order_8812"},
"actor": {"type": "ai_agent", "name": "support-agent"},
"context": {"customer_tier": "business", "ticket": "Customer asks for a refund of a double charge."},
"options": {"language": "auto", "mode": "standard"}
}
| Field | ||
|---|---|---|
action.type |
required | lower case, ^[a-z][a-z0-9_.:-]{0,63}$, in your own vocabulary; the registry of known types (refund, cancel_subscription, send_message, modify_configuration, transfer, delete_records, …) gives Gate's rules something to hold on to |
action.amount, action.currency |
optional | the amount moved, ISO 4217 code; an action that moves money without an amount is answered more_information |
action.target |
optional | the record the action is about, up to 256 characters |
action.description, action.parameters |
optional | free text up to 2,000 characters; any object |
actor.type |
ai_agent (default), human, system |
|
actor.name |
required | the agent's name, 1 to 128 characters; on an environment with registered agents it must be one of them |
actor.permissions |
optional | what the agent declares it may do; a declaration, not authorisation: on an environment with registered agents the stored list decides |
context |
optional | any JSON, or a string: what the agent knows; it reaches the decision model as prepared evidence |
options.language |
optional | auto (default) or a language code |
The answer, always the same shape:
{
"decision_id": "dec_0192f3…",
"decision": "allow",
"confidence": 0.91,
"reason_codes": [],
"missing": [],
"missing_details": [],
"risk": {"financial": 0.12, "fraud": 0.03, "data_loss": 0.0, "security": 0.01, "overall": 0.12},
"semantic": {"evidence_supports_action": 0.93, "action_reversible": 0.88, "human_review_required": 0.06,
"fraud_indicators": 0.03, "data_exfiltration_risk": 0.01, "data_loss_risk": 0.0,
"financial_impact": 0.12, "blast_radius": 0.08},
"policy_results": [{"policy": "high-value-refund", "matched": false, "effect": "review", "reason_code": "HIGH_VALUE_REFUND"}],
"policy_version": "v1",
"language": "en",
"providers": {"decision": "jev"},
"latency_ms": 412,
"created_at": "2026-10-03T09:14:02.118Z",
"action": {"type": "refund", "identified": true, "confidence": 1.0},
"intake": {"path": "structured", "version": "v1", "input_quality": {}, "contradictions": []},
"review": null
}
| Field | Meaning |
|---|---|
decision |
allow: the agent may proceed. review: a person decides. deny: do not do it. more_information: fetch what missing names and ask again |
confidence |
the decision model's own concentration, 0 to 1; deterministic answers (a cap, a permission, a missing amount) carry 1.0 |
reason_codes |
why, as stable codes (table below); empty on a plain allow |
missing, missing_details |
the request fields a rule found absent, as paths (action.amount) and with the reason each is needed; supply them and ask again |
risk |
financial, fraud, data_loss, security and their overall maximum, 0 to 1 |
semantic |
the model's answers to the eight questions, 0 to 1; public names that change only with a version |
policy_results |
every rule that ran, matched or not, with its effect and reason code |
providers |
which models answered: decision, and intake on the intake path |
action, intake |
how Gate read the proposal: the type and its confidence, the path (structured or intake), input quality, contradictions found |
review |
the person's verdict, once given (GET only): approved, denied or changed, by whom, when |
Facts are computed in code and judgment comes from the model: amounts, limits, permissions, caps
and registered agents are never left to a model; the model answers only semantic questions about
the prepared evidence. Any provider failure, timeout or unreadable answer is review with
PROVIDER_UNAVAILABLE, never allow.
Reason codes
| Code | From | Meaning |
|---|---|---|
ACTION_NOT_PERMITTED |
rule, deny | the actor declared permissions and the action type is not among them |
AGENT_UNKNOWN |
rule, deny | the environment enforces registered agents and this actor is not one |
AGENT_NOT_PERMITTED |
rule, deny | the registered agent may not propose this action type |
RATE_LIMITED |
rule, review | the environment reached its daily decision cap; no model was called |
COST_CAP_REACHED |
rule, review | the environment reached its monthly provider cost cap; no model was called |
EVIDENCE_TOO_LARGE |
rule, review | the context is too large to judge |
MISSING_AMOUNT |
rule, more information | an action that moves money has no amount |
HIGH_VALUE_REFUND |
rule, review | a refund above the limit, or whose sum had to be selected among competing candidates |
DESTRUCTIVE_ACTION_TYPE, SENSITIVE_ACTION_TYPE, EXFILTRATION_ACTION_TYPE |
rule, at least review | the action type itself: deletes records, changes bank details or access, moves data out |
IRREVERSIBLE_HIGH_IMPACT |
rule on the answers, review | hard to undo and wide in effect |
FRAUD_INDICATORS, DATA_EXFILTRATION, DATA_LOSS_RISK |
rule on the answers | the model sees signs of fraud, data leaving its boundary, or data loss |
EVIDENCE_INSUFFICIENT |
rule on the answers, review | the evidence does not support the action |
HUMAN_REVIEW_REQUIRED |
rule on the answers, review | the model says a person should look |
LOW_CONFIDENCE |
rule on the answers, review | the model's confidence below the environment's threshold |
HUMAN_REVIEW_RECOMMENDED, REJECT_RECOMMENDED, MORE_INFORMATION_NEEDED |
the model's handling choice | what the model would do with it |
LOW_CONFIDENCE_REJECT |
policy version 2 | the model rejected with low confidence, so a person decides instead |
PROVIDER_UNAVAILABLE |
fail closed | the decision model did not answer in time or at all |
INTAKE_UNAVAILABLE, MISSING_ACTION_TYPE, MISSING_ACTOR, MISSING_TARGET, UNREGISTERED_ACTION, CONTRADICTORY_INPUT, INJECTION_SUSPECTED |
Intake | what Gate could not read in text or arbitrary JSON, a type outside the registry (never allowed automatically), contradictions, text that instructs the evaluator |
Idempotency
Send Idempotency-Key: <1 to 128 characters>. The same key with the same body returns the same
decision for 24 hours; the same key with a different body is refused with 409.
Text and arbitrary JSON
When Työäly has switched Intake on for your environment, the same endpoint takes
Content-Type: text/plain with the text as the body, or application/json with any object or
array. Gate finds the proposal in it: candidates for the action, the amount and the target are
extracted by code, the decision model only selects among them, and what cannot be found is asked
for in missing. Unknown stays unknown: nothing is ever invented. Hints travel as headers,
each at most 128 characters:
| Header | Hint |
|---|---|
X-Tyoaly-Agent |
the agent that proposes, when the input does not say |
X-Tyoaly-Action-Hint |
the action type meant |
X-Tyoaly-Resource-Hint |
the record the action is about |
X-Tyoaly-Language-Hint |
the language of the text |
A version-1 request never costs an intake model call; clean structured input takes the structured path unchanged.
The record: GET /v1/decisions/{decision_id}
The decision as it was answered, from your own organisation only, with review filled in once a
person has given a verdict. Records are kept 13 months; the content of a decision is kept under the
retention mode of the environment (full 30 days, redacted, metadata, zero) and never in
the record itself.
The verdict: POST /v1/decisions/{decision_id}/review
{"outcome": "approved", "reviewer": "anna@company.fi", "note": "sent as proposed"}
approved: the person carried the action out. denied: refused it. changed: did something
else. The latest review of a decision is the one kept. This is advisory mode's ground truth from an
integration that knows what happened; people without one answer the daily digest mail or the
console, and all three land in the same agreement report.
Errors
{"error": "invalid_api_key", "message": "The API key is not valid"}
| Status | error |
|
|---|---|---|
| 401 | missing_api_key, invalid_api_key |
|
| 404 | decision_not_found, not_found |
|
| 409 | idempotency_conflict |
same key, different body |
| 413 | request_too_large |
the body above 256 KB, a review above 16 KB |
| 415 | unsupported_media_type |
a media type Gate does not read |
| 422 | invalid_request, invalid_idempotency_key |
with details naming the fields |
| 429 | throttled at the gateway; retry with backoff | |
| 503 | store_unavailable |
Gate could not record the decision; nothing unrecorded is answered |
Limits and caps
| Body | 256 KB |
| Provider time | 1.5 s per model call; slower is review with PROVIDER_UNAVAILABLE |
| Daily decisions | per environment, default 10,000; above it review with RATE_LIMITED |
| Monthly provider cost | per environment, $2 test and $20 live by default; above it review with COST_CAP_REACHED |
Example
curl -sS https://api.tyoaly.com/v1/gate/evaluate \
-H "Authorization: Bearer $GATE_KEY" -H "Content-Type: application/json" \
-d '{"action": {"type": "refund", "amount": 49, "currency": "EUR", "target": "order_8812"},
"actor": {"type": "ai_agent", "name": "support-agent"},
"context": {"ticket": "Customer asks for a refund of a double charge."}}'
curl -sS https://api.tyoaly.com/v1/gate/evaluate \
-H "Authorization: Bearer $GATE_KEY" -H "Content-Type: text/plain" \
-H "X-Tyoaly-Agent: support-agent" \
--data-binary 'Please refund order 8812, 49 euros, the customer was charged twice.'