Documentation

Calling the gateway

One HTTP endpoint runs a decision. Send a reference to a published schema and the inputs it declares, and you get the decision back.

POST https://app.ellaworks.ai/api/proxy/{orgSlug}/{environmentName}/decisions

orgSlug is your organization's slug and environmentName is the environment to run in, which decides the variables the instructions can use and the providers the run falls back across.

Authentication

Send an organization API key in either header:

  • X-API-Key: YOUR_API_KEY
  • Authorization: Bearer YOUR_API_KEY

Create keys under Settings, Account, API Keys. The key's owner needs access to the environment, as for reading it: a person who has only dev cannot run decisions in prod, and neither can their key. A key issued by agx login is scoped to other work and is refused here with 401.

The request

{
  "schema": "support/duplicate-charge@1",
  "inputs": { "ticket": "I was charged twice for my order." },
  "metadata": { "orderId": "o-1" }
}
Field
schemaThe reference of a published schema. See Versions and references.
schemaDefinitionInstead of schema: a schema sent inline, checked like a published one and logged as Unpublished (ad hoc): with no name or version. Send exactly one of the two. Use it for tests and one-off questions, and a published schema for anything that has to be versioned.
inputsA value for each input the schema declares. Every required one must be there.
providerOptional. The name of one of your LLM providers. When set, the run goes to that provider only and never falls back.
modelOptional. A model of that provider, with the same effect.
metadataOptional. Up to 20 entries, each a string (key up to 64 characters, value up to 512). It is kept in the decision log in every privacy mode and never sent to a provider, so keep personal data out of it. teamId and runId are the names the log's team and run columns read.
timeoutMsOptional. The time each provider gets, from 1 to 50000. A whole run is held to 50 seconds however many providers are tried.

Leave out provider and model to let the environment choose and to get a fallback when a provider fails.

The answer

{
  "id": "0b0c3f2e-1c4e-4f5b-9a37-2f6f8a1d9c11",
  "decision": true,
  "probabilities": { "true": 0.93, "false": 0.07 },
  "confidence": 0.86,
  "provider": "openai",
  "model": "gpt-6-luna",
  "schema": {
    "ref": "support/duplicate-charge",
    "version": "1.2.0",
    "contentHash": "sha256:9d0e…"
  },
  "latencyMs": 412,
  "attempts": [
    {
      "llmProviderId": "…",
      "provider": "openai",
      "model": "gpt-6-luna",
      "status": "success",
      "latencyMs": 398
    }
  ],
  "costUsd": 0.0001
}
  • decision is true or false for a yes/no, an option id (or a list of ids) for a pick, and a number for a score.
  • probabilities has a key for each outcome: true and false, each option id, or each level of the scale.
  • confidence runs from 0 (a coin flip) to 1. Act on a decision only when it is high enough for you; below that, hand the case to a person.
  • id is the run's row in the decision log (Logs, Decisions).
  • attempts lists every provider tried, in order, with how each went. latencyMs is the whole run, fallbacks included.
  • costUsd is an estimate at list price and is left out for a model that is not priced.

curl

curl -X POST "https://app.ellaworks.ai/api/proxy/acme/prod/decisions" \
  -H "X-API-Key: $ELLAWORKS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "schema": "support/duplicate-charge@1",
    "inputs": { "ticket": "I was charged twice for my order." }
  }'

TypeScript

interface DecisionAnswer {
  id: string;
  decision: boolean | string | string[] | number;
  probabilities: Record<string, number>;
  confidence: number;
  provider: string;
  model: string;
  schema: { ref: string | null; version: string | null; contentHash: string };
  latencyMs: number;
}

async function decide(
  schema: string,
  inputs: Record<string, string | number | boolean>,
): Promise<DecisionAnswer> {
  const response = await fetch(
    "https://app.ellaworks.ai/api/proxy/acme/prod/decisions",
    {
      method: "POST",
      headers: {
        "X-API-Key": process.env.ELLAWORKS_API_KEY ?? "",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ schema, inputs }),
    },
  );
  if (!response.ok) {
    const failure = (await response.json()) as { error: string; code?: string };
    throw new Error(`Decision failed (${response.status} ${failure.code ?? ""}): ${failure.error}`);
  }
  return (await response.json()) as DecisionAnswer;
}

const answer = await decide("support/duplicate-charge@1", {
  ticket: "I was charged twice for my order.",
});
if (answer.decision === true && answer.confidence >= 0.8) {
  // refund automatically
} else {
  // send to a person
}

Python

import os
import requests

URL = "https://app.ellaworks.ai/api/proxy/acme/prod/decisions"


def decide(schema: str, inputs: dict) -> dict:
    response = requests.post(
        URL,
        headers={"X-API-Key": os.environ["ELLAWORKS_API_KEY"]},
        json={"schema": schema, "inputs": inputs},
        timeout=60,
    )
    if not response.ok:
        failure = response.json()
        raise RuntimeError(
            f"Decision failed ({response.status_code} {failure.get('code', '')}): {failure['error']}"
        )
    return response.json()


answer = decide("support/duplicate-charge@1", {"ticket": "I was charged twice for my order."})
if answer["decision"] is True and answer["confidence"] >= 0.8:
    ...  # refund automatically
else:
    ...  # send to a person

Errors

A failure is { "error": "…", "code": "…" }, with more fields where noted.

StatusMeaningWhat to do
400The body is not valid, the reference is malformed (invalid_schema_ref), required inputs are missing (missing_inputs, with missingInputs), or a provider rejected the request itself (request_rejected, with attempts)Fix the request. Retrying the same one fails the same way.
401No key, a bad key, or a scoped keyCheck the key.
403You have no access to the environment, or to the private registry the schema is in (forbidden)Ask an administrator for the grant.
404The schema, the environment or the named provider does not existCheck the names.
413The body is over 1 MBSend less.
422No provider you have configured can run this schema (no_compatible_provider)Add a provider, or change the schema. Retrying does not help.
500A provider key could not be read, or a lookup failed on our sideRetry later, with a pause.
502Every provider failed (all_providers_failed, with attempts)Retry later, with a pause. The attempts say what each one answered.

Good habits

  • Set a timeout in your client a little above what you pass as timeoutMs, or above 50 seconds if you pass none, and decide in advance what your code does when no decision arrives.
  • Check confidence before acting, and keep a path for the cases below your bar.
  • Pin the version in production. See Versions and references.
  • Do not put personal data in metadata. What the log keeps of inputs is an administrator's setting; metadata is always kept.