One bearer token, one POST, and a human goes and looks. What comes back is structured evidence your agent can act on — not a photograph somebody has to interpret.
Base URLhttps://tagteam-api.successagenticlabs.com/v1
Quickstart
Register, throw one Tag, read the result. Three calls, and the third one is the product. Everything else in this reference is detail you can reach for later.
Expansion happens on the server and produces exactly the Tag POST /v1/tags would have produced — same funding path, same guardrails, same verification, same payout. This endpoint saves typing, not rules.
A null content verdict is reported as null. It is never rendered as a pass.
Pass ?max_age_minutes=N to have the response judge its own freshness. Age is measured from capture — when the world was observed — not from upload or verification, and is computed on every read rather than stored.
Evidence older than max_age_minutes is returned anyway, flagged rather than withheld. A six-hour-old observation may be fine for one decision and useless for another, and that is your call.
Agent identity and authority
The question an enterprise asks about an autonomous agent is not "can it do this?" but "is it allowed to?". An API key here is an identity: it has an owner, its own ceilings, the permissions it holds, what it has spent, and a record. Every limit is enforced server-side inside the transaction that charges the card — an agent can lower its own ceilings and can never raise them.
GET/v1/agents/meAPI key
Who this key is, what it may do, and what it has spent.
trust_score is null until at least 20 missions have completed. Null means "not enough history to say", never "untrustworthy" — a score invented from three missions is a number a buyer would act on and it would mean nothing.
A key created before agent identity existed returns agent: null and says so. It is not treated as an agent with no limits.
POST/v1/missions/evaluateAPI key
Would this mission be allowed? Reserves nothing, charges nothing.
This runs the identical function that gates the money, in dry-run mode. One implementation on purpose: a preview that could disagree with the real decision would be worse than no preview, because an agent would plan around it.
Three outcomes: approved, blocked, requires_approval. Blocking checks all run before the approval question — asking a person to approve something that would be refused anyway is how an approval queue becomes a rubber stamp.
An API key can only ever tighten: lower a ceiling, drop a scope, add a Recipe restriction. Every widening move is refused with 403.
Removing a ceiling counts as raising it, because null means no limit. Raising an approval threshold counts as widening too — a higher threshold means more happens without a person.
Loosening is done by a person signed in to the dashboard. A key that could raise its own limit would not be a limit.
POST/v1/approvals/{id}/decideCorner sign-in
A person approves or rejects a held mission.
Request
{ "decision": "approve", "note": "Confirmed with the client." }
A mission above its agent’s approval threshold is not created and not charged. It is held, and the agent receives 202 with an approval id rather than an error.
Approval throws the Tag through the ordinary path, re-running every check at that moment. The world moves while a request waits, and approving a week-old mission without re-checking budgets, kill switches and cards would make this queue a hole in every other control.
An API key cannot approve a mission — including its own. That is the entire purpose of the threshold.
Plan before you spend
Three endpoints that create nothing and charge nothing. An agent that cannot estimate has to guess or ask a human, and both are worse than asking us — so these answer "what would it cost, how long would it take, and can you even do it here?" before anything is committed.
POST/v1/capabilities/searchAPI key
Describe the work in your own words; find out which Recipe does it.
Request
{
"intent": "check whether this restaurant is still open",
"location": { "lat": 33.749, "lng": -84.388 }
}
This is lexical matching against a hand-written vocabulary, not a language model. It is built to be honest about that rather than to disguise it.
Nothing fits? You get an empty matches array and a plain explanation — never the closest guess. A wrong Recipe spends real money observing something you did not ask about.
Two Recipes fitting equally well returns both with ambiguous: true. "Check the signage" genuinely could be two operations; you know which you meant and we do not.
unrecognised_terms lists the words that meant nothing to us, so you can see what was actually weighed rather than assuming it was understood.
POST/v1/quotesAPI key
What a Tag would cost and how long it would plausibly take.
{
"price": {
"bounty_cents": 7500,
"platform_fee_cents": 1500,
"total_charge_cents": 9000,
"partner_payout_cents": 7500,
"exact": true
},
"estimated_completion": {
"median_minutes": null,
"basis": "no_history",
"observations": 0,
"note": "No Tag has been completed near here yet…"
},
"coverage": { "servable": false, "partners_nearby": 0 }
}
The price is exact, not an estimate: it comes from the same function that charges the card, so a quote cannot disagree with the invoice.
The timing is history or it is null. Where too few Tags have completed nearby, median_minutes comes back null with basis "no_history" rather than a plausible number — an agent planning around an invented estimate is worse off than one told we do not know.
A quote reserves nothing. It does not hold a price, a Partner or capacity, and nothing is charged until a Tag is thrown.
GET/openapi.jsonPublic
The machine-readable OpenAPI 3.1 description of this API.
Generated from the running route table, so it cannot describe an endpoint that does not exist — and a build check fails if a public endpoint ships without a description, or if a description outlives the endpoint it documents.
Covers the agent-facing API. Administrator, Partner and dashboard routes are deliberately outside it, each excluded for a stated reason rather than by being forgotten.
Point any OpenAPI client generator at it.
GET/v1/capabilitiesAPI key
Every Recipe, its proof requirements, and whether this place can be served.
Pass lat and lng to ask about a place; omit both to describe the platform.
An uncovered area returns servable: false and still lists every Recipe. The work is supported; the location is not — and conflating those would send you to a different provider when you needed a different city.
Each Recipe carries its version, checklist and prohibited actions, so an agent can read what a Partner will and will not do before choosing one.
Authentication
One bearer token on every request. Keys are stored as a peppered SHA-256 hash — a database dump does not yield working credentials.
GET/v1/corners/meAPI key
Your plan, quota and spending limits.
PATCH/v1/corners/me/limitsAPI key
Lower your own spending limits.
Limits can be lowered through the API; raising them needs a human. That asymmetry is deliberate — if this key leaks, the attacker must not be able to raise its own ceiling.
GET/v1/limitsAPI key
Per-Recipe spending ceilings.
PUT/v1/limits/recipesAPI key
Set a ceiling for one Recipe.
Throwing Tags
A Tag is one unit of physical work at one place, funded before anyone is asked to go there. If the charge fails you get a 402 and no open Tag is created — a Partner must never do real work for money that is not there.
POST/v1/tagsAPI keyIdempotent
Throw a Tag, stating the work yourself.
Request
{
"title": "Photograph storefront and street number",
"description": "Photograph the storefront, the visible street number, and the entrance.",
"category": "verification",
"bounty_cents": 7500,
"geo": { "lat": 33.749, "lng": -84.388, "radius_m": 150 },
"required_proof": { "photos": 3, "checklist": ["storefront"], "notes_required": true },
"expires_at": "2026-09-13T18:00:00Z",
"urgency": "standard"
}
GET/v1/tagsAPI key
List your Tags.
GET/v1/tags/:idAPI key
Where one Tag is now.
POST/v1/tags/:id/decisionAPI keyIdempotent
Approve or reject submitted proof.
Approving a Tag whose PLACE or TIME lock failed returns 422. Those are facts about the world, not opinions about quality, and only an administrator can override them — which creates a permanent audit record.
POST/v1/tags/:id/cancelAPI keyIdempotent
Cancel before anyone claims it.
POST/v1/tags/:id/refundAPI keyIdempotent
Refund an expired or unclaimed Tag.
Recipes
Twelve standard jobs whose requirements are stated in advance and can therefore be verified against. Recipes are versioned, never overwritten: a Tag records the slug and the version, so one thrown three weeks ago is still judged against the requirements it was actually given.
GET/v1/recipesAPI key
Every published Recipe.
GET/v1/recipes/:slugAPI key
One Recipe and its requirements.
POST/v1/tags/recipeAPI keyIdempotent
Throw a Tag from a Recipe.
Proof and verification
Proof is captured inside a server-issued capture session. The server records the location fixes itself and stamps the time from its own clock — a client that says it was somewhere is not evidence that it was.
GET/v1/tags/:id/evidence-packetAPI key
The durable record, including what it does not prove.
GET/v1/tags/:id/ledgerAPI key
Every cent for one Tag, with Stripe object ids.
Campaigns and schedules
A campaign of 200 locations is 200 ordinary Tags. Combining them would be easier and would destroy the only thing worth paying for: you could no longer say which of the 200 places was visited, by whom, or whether that specific one was paid for.
POST/v1/campaignsAPI keyIdempotent
Create a campaign across many locations.
GET/v1/campaignsAPI key
List campaigns.
GET/v1/campaigns/:idAPI key
Campaign progress and spend.
GET/v1/campaigns/:id/locationsAPI key
Per-location status.
POST/v1/campaigns/:id/cancelAPI keyIdempotent
Stop a campaign.
POST/v1/schedulesAPI keyIdempotent
Recurring Tags with a hard budget.
A schedule stores a rule and a single next run. Creating a year of Mondays up front would mean charging a year of money up front, and would let one malfunctioning call commit you to 52 weeks of spending.
GET/v1/schedulesAPI key
List schedules.
GET/v1/schedules/:idAPI key
One schedule and its runs.
POST/v1/schedules/:id/pauseAPI key
Pause a schedule.
POST/v1/schedules/:id/resumeAPI key
Resume a schedule.
Supply and coverage
Ask before you spend. Both of these answer honestly about places we cannot serve — a confident yes that produces an expired Tag costs more trust than a plain "not yet".
GET/v1/partners/searchAPI key
How much supply exists near a point.
Counts, never identities.
GET/v1/coverageAPI key
Whether we can actually serve a place.
Disputes
Opening a dispute stops both the payout and the refund until a human resolves it.
POST/v1/disputesAPI keyIdempotent
Freeze a Tag pending human review.
GET/v1/disputes/:idAPI key
Where a dispute stands.
Operational status
Call this before a long autonomous run. Knowing the money engine is paused is more useful than discovering it on your next POST /v1/tags.
Same key, same body → the original response, with Idempotent-Replay: true.
Same key, different body → 409 IDEMPOTENCY_CONFLICT.
Key still in flight → 409 IDEMPOTENCY_IN_PROGRESS. Retry in a few seconds.
Tag lifecycle
This table is generated from the same constant the server enforces, so it cannot drift from reality. A transition that is not listed returns 409 INVALID_TRANSITION — and the database refuses it too, with a trigger, so a bug in a future handler cannot move money sideways.
From
May become
draft
fundingcancelled
funding
opencancelled
open
claimedexpiredcancelledrefund_pending
claimed
submittedexpireddisputed
submitted
in_reviewdisputed
in_review
verifiedopendisputed
verified
paidrefund_pending
paidfinal
Nothing. The Tag is finished and no further money may move.
cancelledfinal
Nothing. The Tag is finished and no further money may move.
expired
refund_pending
disputed
verifiedrefund_pending
refund_pending
refunded
refundedfinal
Nothing. The Tag is finished and no further money may move.
Two states exist that a simplified diagram usually omits, and both matter for money: funding is where the card is charged before a Tag is ever visible, and refund_pending is how every funded Tag reaches exactly one financial resolution — paid or refunded, never neither.
Webhooks
Your agent does not poll. Every delivery is signed with the secret shown once at registration; verify it before trusting the body.
Event
What it means
tag.claimed
A Partner accepted it. The clock is running.
tag.submitted
Proof is in and being verified.
tag.verified
All three locks held.
tag.paid
The Partner was paid. The result is final.
tag.rejected
Verification failed. Nobody was paid.
tag.expired
Nobody claimed it in the window. Your money is being returned.
tag.refunded
The refund settled.
tag.disputed
Money is frozen until a human rules.
Deliveries retry with backoff on the Contender and Champion plans. A delivery that never succeeds does not change what happened — the Tag's state and its ledger are the record, and both are readable over the API.
Errors
Every error has the same shape and a stable machine-readable code. Agents branch on the code; humans read the message.
A ceiling was hit. Limits are raised by a human, not by the API. Stop.
NOT_FOUND
404
Wrong id, or not yours.
INVALID_TRANSITION
409
The Tag is not in a state where that makes sense.
ALREADY_CLAIMED
409
Another Partner got there first.
IDEMPOTENCY_CONFLICT
409
Same key, different body. Use a new key.
IDEMPOTENCY_IN_PROGRESS
409
Still running. Retry in a few seconds.
DISPUTE_FREEZE
409
Money is frozen until a human resolves the dispute.
VERIFICATION_FAILED
422
The Locks did not pass. Read details.failures.
UNSAFE_TASK
422
The request describes work we will not send a person to do.
RATE_LIMITED
429
Back off.
KILL_SWITCH
503
Operations paused this capability. Check GET /v1/status. Do not loop.
Rate limits and spending controls
Two different things, often confused. A rate limit protects the service. A spending limit protects you.
Rate limiting is per API key, per minute. Over it you get 429 RATE_LIMITED — back off and retry.
Spending ceilings exist per Tag, per day, per month, per hour, per campaign, per schedule and per Recipe. Over one you get 403 SPENDING_LIMIT and no charge is made.
Every ceiling is enforced inside the transaction that charges the card. The SDK and the MCP server are interfaces, not authorities — neither can raise a limit, and neither touches the database.
A 403 SPENDING_LIMIT is not a retryable error. It means a human decision is required, and an agent that loops on it will simply keep being refused.