Skip to main content
The API was designed so an agent can run a deal desk: notice what changed, decide what to do, act, and hand off to a human where judgement is needed. This page shows how the pieces fit and walks through one complete loop. For a chat-driven version of the same thing, see the MCP server.

The shape of an agent

  1. Webhooks are the triggers. Subscribe to the events you care about and treat each delivery as “something changed on deal X”. Never poll for changes.
  2. Reads give context. GET /v1/deals/{id}?include=business,people,offers,submissions,documents,activity returns the full picture in one call.
  3. Lender matching is the recommender. You do not need to encode lender criteria in your agent; the location already maintains them.
  4. Writes are idempotent. Every action that sends something takes an Idempotency-Key, so an agent that crashes and restarts cannot email a lender twice.
  5. Merchant follow-up is one call. Portal links and document requests reach the merchant through the channels the location already has.

Building blocks

Triggers

See Webhooks for verification and retries.

Recommender

GET /v1/deals/{id}/lender-matches runs the location’s criteria against the deal and explains itself.
  • view=recommended is the shortlist: score at or above the location threshold with the data to support it.
  • view=eligible adds lenders that clear every hard gate but score lower. Use it when the shortlist is empty.
  • auto_eligible is the flag to gate automation on. It means recommended and no unknown gates, so nothing about the deal could flip the result once the missing data arrives.
  • checks[] carries one entry per criterion with kind (gate or factor), what the lender requires, what the deal has, and whether it passed or was skipped for lack of data. It is written to be shown to a user or pasted into a prompt.
  • unknown_gates and meta.deal.missing_fields tell you what to ask the merchant for.

Submitting

POST /v1/deals/{id}/submissions with lender_ids, an optional document_ids list, and an Idempotency-Key. Use the webhook event id that triggered the decision as the key: the same event can then never produce two sends. Pass "send": false to record a proposed submission for a rep to send from the app.

Merchant follow-up

  • POST /v1/deals/{id}/portal-link with send_via email, sms or none issues a 24-hour magic link to the merchant portal, where the merchant can upload documents and see shared offers. sent, sent_via and sent_to in the response tell you whether it went out; a failed send still returns the link with sent: false and meta.warning. expires_in_days is accepted but ignored in v1. Requires portal:send.
  • POST /v1/deals/{id}/document-requests asks for specific documents. See Documents and uploads.

Inbound calls and texts

When an unknown number calls or texts, resolve it first. Encode the + as %2B.
Response 200
The list returns the person only. GET /v1/people/{id} adds their businesses (with role and ownership) and the deals they own:
Response 200
Then GET /v1/deals/{id}?include=offers,submissions,documents for what to say. An empty data array from the lookup means a genuinely new contact; create the person with POST /v1/people and continue.

Guardrails

  • Scopes. Give the agent’s key only the scopes its job needs. A reader that summarises deals needs no write scope at all. A missing scope is a clear 403 with code: "missing_scope", which an agent can surface as “I am not allowed to do that here”.
  • Roles. Prefer manager (the default); it cannot change settings or the team. Use user for an agent that should only see the deals it created.
  • Idempotency everywhere. Any POST that sends email or SMS gets an Idempotency-Key derived from the triggering event. Retries then replay instead of re-sending.
  • Rate limits. 300 requests per minute per credential. Fan out across deals with a queue, not a loop; a 429 carries Retry-After.
  • Stage rules. POST /v1/deals/{id}/stage enforces the board’s rules; a refused move returns 400 with code: "stage_rule" and the same message a rep would see. Do not work around it.
  • Human approval where money moves. Submit automatically only for auto_eligible matches; route the rest, and every offer decision, to a rep.
  • Log request ids. Store X-Request-Id with every action so a wrong send can be traced.

Worked example

An underwriting agent that runs from “deal created” to “offer picked”.
1

deal.created arrives

Fetch the deal with ?include=business,people,documents. Run GET /v1/deals/{id}/lender-matches?view=recommended. If meta.deal.completeness_percent is low or no bank statements are on file, request them:
2

statement.analyzed arrives

Read GET /v1/deals/{id}/statement-analysis for NSFs, negative days and positions. Re-run lender matches. Filter to auto_eligible: true, sort by score and approval_rate, keep the top three.
3

Submit

Add a note explaining the choice so the rep sees the reasoning on the deal:
4

offer.received arrives

GET /v1/deals/{id}/offers, compare payback and daily payment against the positions from statement analysis, and create a task for the rep with the comparison (POST /v1/deals/{id}/tasks). The rep picks in the app, or the agent calls POST /v1/offers/{id}/primary when the rule is explicit.
5

Send the merchant the portal link

The merchant uploads what the lender still needs; document.uploaded brings the loop back to the agent.
6

Later: advance.renewal_ready

POST /v1/deals/{id}/renewal creates the linked renewal deal on the same business, and the loop starts again from deal.created.