Buying results

Budgets and approvals

Per-agent budgets and approval thresholds, enforced on our side: what happens when a purchase needs a person.

An agent spends your money, so every agent has limits it can't change: a monthly budget and an approval threshold. Both are checked on our side before anything is held, on every request. Going over either doesn't fail the purchase: it asks an owner, and the agent carries on once they say yes.

The limits

Per-agent limits
LimitDefaultWhat it does
monthly_budget_credits50,000 credits ($50) per UTC calendar monthA live purchase that would take the agent's spend this month over its budget needs approval (over_budget). At 80% the owners get a budget.80pct event and email.
approval_threshold_credits20,000 credits ($20)A single request whose price × items is above it needs approval (over_threshold). null means never ask. The dashboard offers $5, $20, $50 or never.
ip_allowlistempty (any IP)IP addresses or CIDR ranges the key may be used from. Anything else gets 403 ip_not_allowed.

Set them on the Agents page, or with PATCH https://api.arettic.com/v1/orgs/{orgId}/agents/{agentId} (a member, or a write org key). An agent key can read its own limits (GET /v1/agent, GET /v1/balance) but never change them.

curl
curl -X PATCH https://api.arettic.com/v1/orgs/$ORG_ID/agents/$AGENT_ID \
  -H "Authorization: Bearer $ARETTIC_ORG_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "monthly_budget_credits": 200000, "approval_threshold_credits": 50000 }'

What is checked, in order

  1. Balance. The org can't pay for price × items: declined, 402 insufficient_credits. No approval can fix that; top up.
  2. Trial limit. An org that has never topped up can spend at most 50 trial credits an hour: declined, 429 trial_limit.
  3. Approval threshold, then monthly budget: over either, the request becomes an approval (below).

The hold step checks the budget again under a lock, so two requests sent at the same moment can't both slip under it.

When a purchase needs approval

The request is not run and nothing is held. The answer is HTTP 202:

Response
{
  "status": "approval_required",
  "approval_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "reason": "over_threshold",
  "amount": { "credits": "24000", "usd": "24.000" },
  "expires_at": "2026-10-01T11:10:00.000Z",
  "message": "This is above the agent's approval threshold. An owner has been asked to approve it; retry with approval_id once approved."
}
  1. Every owner gets an email with a one-click link to approve or decline, no sign-in needed. They can also decide on the Approvals page, or with POST /v1/orgs/{orgId}/approvals/{id}/decision and { "decision": "approve" } or "reject" (optional note). Webhook endpoints get approval.requested and then approval.decided.
  2. The agent checks GET /v1/approvals/{id}: status is pending, approved, rejected, expired or used, with reason, agent, tool, items, amount, expires_at, decided_at and decision_note.
  3. Once approved, the agent sends the same request again with approval_id. It runs without the threshold and budget checks, for exactly the tool, inputs and amount that were approved.
TypeScript
import { Arettic } from "@arettic/sdk";

const arettic = new Arettic();
const request = { tool_id: "peopledatalabs-enrich-company", inputs };
let result = await arettic.execute(request);
if (result.status === "approval_required") {
  const approval = await arettic.waitForApproval(result.approval_id); // polls every 5 s, up to 24 h
  if (approval.status !== "approved") throw new Error(`Not approved: ${approval.status}`);
  result = await arettic.execute({ ...request, approval_id: result.approval_id });
}
Python
import time

from arettic import Arettic

client = Arettic()
request = {"tool_id": "peopledatalabs-enrich-company", "inputs": inputs}
result = client.execute(request)
if result["status"] == "approval_required":
    approval_id = result["approval_id"]
    while (approval := client.approval(approval_id))["status"] == "pending":
        time.sleep(5)
    if approval["status"] != "approved":
        raise RuntimeError(f"Not approved: {approval['status']}")
    result = client.execute(request, approval_id=approval_id)

Over MCP, execute returns the same approval_required answer and get_approval checks it; see MCP.

Other guards

Agents and seats per plan
PlanAgentsSeats
Pay as you go21
Pro105
Maxno limitno limit

Updated 2026-09-30 · This page as Markdown · JSON