Authentication
One base URL, one bearer key. Sandbox keys are free at signup; live keys at launch.
# base URL
https://api.getappen.com
# every request
Authorization: Bearer sk_test_…
Quickstart
Three calls from nothing to a funded card.
# 1 — create a rulebook
POST /v1/rulebooks
{
"name": "field-techs",
"rules": [
{ "kind": "merchant_lock", "config": { "allow": ["ferguson.com"] } },
{ "kind": "amount_ceiling", "config": { "max_cents": 100000 } },
{ "kind": "approval_threshold", "config": { "above_cents": 50000 } }
]
}
→ { "id": "rb_8f2a", "rules": 3 }
# 2 — declare what you are about to buy
POST /v1/intents
{ "rulebook": "rb_8f2a", "merchant": "ferguson.com", "amount": 78000 }
→ { "id": "int_31c9", "decision": "review" }
# 3 — a person approves; the card is created and funded
POST /v1/intents/int_31c9/approve
→ { "decision": "approve", "card": { "id": "crd_77b1", "funded_cents": 78000 } }
Verdicts
| Value | Meaning | What to do |
approve | Every rule passed | A funded card exists. Proceed. |
decline | At least one rule refused | Stop. The response names every rule that refused and why. |
review | A human gate tripped | Wait. Poll the intent or listen for the webhook. |
partial | Approved at a lower amount | Read approved_cents before proceeding. |
The rule object
Every rule is a kind and a config. Unknown keys are rejected at
write time rather than silently ignored.
{
"kind": "spend_cap",
"config": {
"max_total_cents": 500000,
"window": "week", // "day" | "week" | "month" | seconds
"allow_partial": true // approve up to the remaining headroom
}
}
Branching and loops
condition is a pure if/else — children marked "when": "pass" run when it
is true, "when": "fail" when it is false, and a false condition never declines the card.
repeat is a while loop over the payment stream: iterations are counted from captured ledger entries,
and once the loop is complete it declines.
# branch on amount, then run a 4-payment installment loop
[
{ "kind": "condition", "config": { "when_amount_over_cents": 10000 },
"children": [
{ "kind": "approval_threshold", "when": "pass", "config": { "above_cents": 0 } },
{ "kind": "velocity", "when": "fail", "config": { "max_count": 5, "window_sec": 86400 } }
] },
{ "kind": "repeat", "config": { "times": 4, "total_cents": 100000, "merchant": "landlord.com" },
"children": [
{ "kind": "amount_ceiling", "when": "pass", "config": { "max_cents": 25000, "allow_partial": true } }
] }
]
Webhooks
Two directions. We call you to ask, and we call you to tell.
# we ask — webhook_check, before any funding
POST https://you.example.com/appen
{ "intent": "int_31c9", "merchant": "ferguson.com", "amount": 78000 }
# you answer with whatever your business knows
{ "decision": "approve" }
# we tell — every state change
intent.declared intent.approved intent.declined
intent.review card.funded card.closed
The audit trace
Every rule runs on every intent. There is no short-circuit, which is why a decline returns
all of its reasons and not just the first.
{
"decision": "decline",
"results": [
{ "kind": "merchant_lock", "status": "pass", "detail": "'ferguson.com' matches allow-list" },
{ "kind": "amount_ceiling", "status": "fail", "detail": "$1,240.00 exceeds $1,000.00 max" },
{ "kind": "spend_cap", "status": "pass", "detail": "$310.00 this week of $5,000.00" },
{ "kind": "duplicate_charge_guard","status": "fail", "detail": "identical charge 4 minutes ago" }
]
}
For agents
An agent gets exit codes it can branch on, and never a card number.
appen buy ferguson.com 780.00
# 0 approved · 1 declined · 2 held for a human · 3 error
agent.txt ·
llms.txt