Methods
These are the methods on the npm package @owctopus/sdk. Create a client, then call decide. getExecution and list read runs that the Owctopus API stored.
The package is TypeScript, ESM only, and has no runtime dependencies. It runs on Node 18 or newer. Import it from your backend. Do not ship the key in a browser.
npm install @owctopus/sdknew Owctopus(options?)
Creates the client. One key is required: an Owctopus key, an OpenAI key, or both.
import { Owctopus } from "@owctopus/sdk";
const owctopus = new Owctopus({
apiKey: process.env.OWCTOPUS_API_KEY,
openaiApiKey: process.env.OPENAI_API_KEY,
baseUrl: "http://localhost:3000",
});| Option | Default | What it does |
|---|---|---|
apiKey | OWCTOPUS_API_KEY | Bearer token for the Owctopus API. Live keys start with ow_live_. Test keys start with ow_test_. |
baseUrl | OWCTOPUS_BASE_URL, then https://api.owctopus.com | Owctopus API root. Use http://localhost:3000 while you develop. |
openaiApiKey | OPENAI_API_KEY only when apiKey is not passed | The customer’s OpenAI key. When this is set, decide() calls OpenAI and does not call the Owctopus API. |
openaiBaseUrl | https://api.openai.com/v1 | OpenAI-compatible root. The client posts to chat/completions. |
models | fast and balanced are gpt-4o-mini. careful is gpt-4o. | Model ids for each tier. Pass only the tiers you want to change. |
environment | none | Sent as the owctopus-environment header when a project has more than one environment. |
timeoutMs | 30000 | Aborts the request after this many milliseconds. |
fetch | global fetch | Replace the HTTP client, usually in tests. |
If you pass apiKey, the client does not read OPENAI_API_KEY. Pass openaiApiKey as well when you want OpenAI. The constructor throws when neither key is set. Keys are not written into results or error messages.
decide(input)
Runs one decision. An Owctopus key sends POST /v1/executions. The API looks up a stored condition for this decision, context, and option list. A match returns that choice and does not call AI. No match calls AI, then stores the choice as the condition for the next time. Both count as one decision. OpenAI is called from this process only when the client has no Owctopus key, and that path does not store a condition.
const result = await owctopus.decide({
decision: "shipping_method",
context: { cartValue: 4500, customerType: "premium", urgency: "high" },
options: [
{ id: "standard", nextAction: "ship_standard" },
{ id: "express", nextAction: "ship_express" },
{ id: "same_day", nextAction: "ship_same_day" },
],
});
if (result.status === "decided" && result.option) {
// result.option is "standard", "express", or "same_day"
}| Field | Required | What it does |
|---|---|---|
decision | Yes | A name, such as shipping_method, meal_suggestion, or daily_workout. |
context | Yes | Any object. Owctopus does not fix the fields. Your app sends the facts it has for this moment. |
options | Required for OpenAI | A string id, or { id, attributes?, nextAction? }. The chosen id comes back as result.option. |
version | No | Decision version to run on the Owctopus API. Omitted uses the latest release in that environment. |
constraints | No | Sent to the Owctopus API. Not used on the OpenAI path. |
idempotencyKey | No | Sent as idempotency-key. The client generates sdk_ plus 32 hex characters when you omit it, and reuses that key on retries. |
model | No | An OpenAI model id. "auto" lets the client choose. Ignored on the Owctopus API path. |
On the OpenAI path the client picks a model before it sends the request:
- A
modelother than"auto"is used as written. - A name containing fraud, risk, credit, compliance, legal, or security uses
gpt-4o. So does a context larger than 8,000 bytes, or more than six options. - One to three options and a context under 1,500 bytes uses
gpt-4o-mini. - Everything else uses the balanced model, which is also
gpt-4o-miniunless you changemodels.
Option attributes are not sent to the model. The user message includes the decision name, the context, and the option ids. If the reply does not name one of those ids, status is needs_review and option is null.
An OpenAI result uses an executionId that starts with local_. result.learning.stored is false, and condition.updates is empty. Saving that record, and showing how a condition later changes, is not part of this call yet.
What decide() returns
Both paths return the same result shape. OpenAI fills openai and learning. The Owctopus API does not.
| Field | Meaning |
|---|---|
executionId | Id of the stored run, or local_… when OpenAI answered and nothing was stored. |
requestId | Id for this request. On OpenAI this is the chat completion id. |
decision | The name you sent. |
version | Version that ran. 0 on an OpenAI call that did not pass version. |
status | decided, needs_review, rejected, failed, or timeout. |
option | The chosen option id, or null. |
score | Score for the chosen option, or null. |
confidence | Number from the model, or null. |
risk | low, medium, high, or unknown. OpenAI returns unknown. |
reasons | Short strings explaining the choice. |
alternatives | Other options with a score. Each item is { option, score, value }. |
nextAction | nextAction from the chosen option, when you sent one. |
objective | { type, metric, optionId, value }. OpenAI uses type select. |
facts | Facts the Owctopus API attached. Empty on the OpenAI path. |
model | { provider, model, fallback }, or null. |
trace | Stages that ran, each with stage, status, message, and elapsedMs. |
openai | The request messages and the reply content, id, model, and token counts. The API key is not included. |
learning | Decision, context, options, choice, confidence, model, and condition. stored is false until a later release saves it. |
getExecution(executionId)
Reads one stored run with GET /v1/executions/:id. This needs an Owctopus API key. An id from an OpenAI-only decide() is not in the database, so this call will not find it.
const again = await owctopus.getExecution(result.executionId);list(query?)
Reads recent runs with GET /v1/executions. This needs an Owctopus API key. The response is { data: DecisionResult[] }.
const recent = await owctopus.list({
decision: "shipping_method",
status: "decided",
limit: 20,
});| Field | What it does |
|---|---|
decision | Only runs for this decision name. |
status | One of decided, needs_review, rejected, failed, timeout. |
limit | How many runs to return. |
selectModel(input, models?)
Returns the model decide() would use for this input. It does not call the network. The result is { tier, model, reason }. tier is fast, balanced, or careful.
import { selectModel } from "@owctopus/sdk";
const choice = selectModel({
decision: "shipping_method",
context: { cartValue: 4500 },
options: ["standard", "express", "same_day"],
});
// choice.model is "gpt-4o-mini"Errors
A failed call throws OwctopusError. Check error.code, error.status, and error.requestId. error.executionId is set when the Owctopus API stored the run before it failed. The client retries 408, 429, and 503 up to three times with the same idempotency key. A timeout is not retried.
import { AuthenticationError, OwctopusError, ValidationError } from "@owctopus/sdk";
try {
await owctopus.decide({
decision: "shipping_method",
context: { cartValue: 4500 },
options: ["standard", "express"],
});
} catch (error) {
if (error instanceof ValidationError) {
// error.code is OPTIONS_REQUIRED when OpenAI was called with no options
} else if (error instanceof AuthenticationError) {
// the Owctopus key was rejected
} else if (error instanceof OwctopusError) {
// error.code, error.status, error.requestId
}
}| Class | When |
|---|---|
AuthenticationError | Owctopus API returned 401. |
AuthorizationError | Owctopus API returned 403. |
ValidationError | 400 or 422. OpenAI with no options uses code OPTIONS_REQUIRED. |
DecisionNotFoundError | 404. The execution id is not in this environment. |
IdempotencyConflictError | 409. The same idempotency key was sent with a different body. |
RateLimitError | 429. |
DecisionExecutionError | 502 from Owctopus, or an OpenAI HTTP error. A rejected OpenAI key uses code OPENAI_AUTH. Other OpenAI failures use OPENAI_ERROR. |
TimeoutError | The request exceeded timeoutMs, or the API returned 504. Code MODEL_TIMEOUT. |
OwctopusError | Anything else, including a network failure. Code NETWORK when the host could not be reached. |