Agent Native CRM

POST/v1/companies

Outbound, from a domain to a deal

Four agents share one workspace. They build the list, judge it against your ICP, write the outreach and record what came back. You read the drafts, correct what is wrong, and decide what closes.

Who this is for

If this is not you, one of the other workflows probably is — they share every primitive and differ only in what the agents are pointed at.

The team

A founder or a small sales team running outbound with agents rather than a seat per person.

What they end up with

Every stage move has an activity behind it, and every message had a reader before it went.

The agents, and what each one is responsible for

These are your agents, not ours. This product ships no model and makes no decisions; it is the workspace they share and the record they leave. Each of them reaches it through the same API a person’s dashboard uses.

Prospecting agent

Upserts a company for every domain it finds and adds it to the campaign list.

Brings its own search. This CRM finds nobody.

Qualifying agent

Judges each company against the ICP, writes what it learned, and sets the list member to qualified or rejected with a reason.

Brings its own research and enrichment. A confidence here is its judgement, not our score.

Outreach agent

Puts the message in a task, claims it, sends it, closes it with the outcome and chains the follow-up.

Brings the mailbox. Nothing is sent from here.

Reply agent

Records what came back on the person's timeline and moves the deal to the stage that reply earns.

Brings the inbox it is watching.

The handoff, in the order it happens

Every call named here is one this API serves — a test reads them back against the operation table and fails on anything invented. What an agent does between two of them is its own business, and ours ends at the write.

Read the schema, then shape it

Prospecting agent

Every workspace answers with its own objects, fields, stages and saved views. Guessing a field name costs a round trip, because an unknown field is rejected and the error names the ones that exist. A field added through the API is a real column and behaves like a standard one.

  • GET /v1/schema
  • POST /v1/schema/fields
  • POST /v1/lists
  • A schema_changed activity carrying the diff and who made it
  • One list, holding company records

Upsert every company it found, and add it to the list

Prospecting agent

The write is an upsert on the domain, so running the same search twice writes nothing the second time — it answers 200 with created: false. Members are upserted on the pair of list and record, so re-sending a batch is free too.

  • POST /v1/companies
  • POST /v1/lists/{id}/members
  • Company records, normalised on the domain
  • An enriched activity on anything that actually changed
  • List members at candidate

Put the research on the queue rather than handing it out

Prospecting agent

Leave the assignee off a task and it belongs to whoever claims it first. That is the whole scheduler: one conditional update inside the workspace, exactly one winner, and a 409 already_claimed for everyone else.

  • POST /v1/tasks
  • GET /v1/tasks
  • One research task per company, unassigned
  • task_created on each company

Claim it, research it, and record the judgement

Qualifying agent

What it learned is written with a source and a confidence, so a later reader can weigh it. A field edit emits field_changed carrying the diff. Nothing is deleted: a company that does not fit stays in the list at rejected, with the reason, so the next campaign does not pay for the same research again.

  • POST /v1/tasks/{id}/claim
  • PATCH /v1/companies/{id}
  • PATCH /v1/lists/{id}/members/{member_id}
  • POST /v1/tasks/{id}/complete
  • field_changed activities with the diff, the source and the confidence
  • Member status qualified or rejected, with the reason it failed
  • task_closed carrying the outcome and the note

Open a deal for the ones that pass

Qualifying agent

A person is keyed on their email or their LinkedIn URL, and one is enough. The deal opens at the first stage of its pipeline; it does not need a stage passed to it, and it will not accept one it does not own.

  • POST /v1/people
  • POST /v1/deals
  • Person records, linked to their company
  • One deal at new on the default pipeline

Queue the message as a task, with the words in it

Outreach agent

The payload is the message: to, subject, body. One send is one task, even inside a thread. A person marked do_not_contact cannot be given one at all — the API answers 409 do_not_contact rather than trusting an agent to remember.

  • POST /v1/tasks
  • One email task linked to the person
  • task_created on that person

Read the draft before it goes

A human

The dashboard renders the task as the message it is — recipient, subject, body — with every follow-up chained behind it as one conversation. Edit the payload, cancel the task, or leave it for the agent to claim.

  • PATCH /v1/tasks/{id}
  • POST /v1/tasks/{id}/cancel
  • Nothing, if you leave it alone. That is the point of a review.

Claim it, send it from its own account, close it with the outcome

Outreach agent

Only the principal who claimed a task may complete it; owners and admins may override. Closing writes task_closed on the linked person carrying the result, which is what makes "we emailed them" a fact with a time on it rather than a memory.

  • POST /v1/tasks/{id}/claim
  • POST /v1/tasks/{id}/complete
  • task_closed on the person, with the outcome and the note

Chain the follow-up to the message before it

Outreach agent

There is no cadence builder. A follow-up is another task pointing at the one it follows through parent_task_id — each send stays independently claimable, and the dashboard still reads it as one ordered thread.

  • POST /v1/tasks
  • A second email task, due later, chained to the first

Record the reply, then move the deal

Reply agent

A stage move is an event with its own route, so it cannot happen without an activity explaining it. stage, status and lost_reason are refused in a PATCH on purpose.

  • POST /v1/activities
  • POST /v1/deals/{id}/stage
  • replied on the person
  • stage_changed from new to contacted

Close it, and say why when it is lost

A human

A loss needs a lost_reason or the call is refused. That one required field is what makes a quarter of losses readable later, which is the only reason anybody reads them.

  • POST /v1/deals/{id}/close
  • status_changed to won or lost, carrying the reason

GET/v1/activities

Every stage move has an activity behind it, and every message had a reader before it went.

Where a person steps in

None of these is a setting somebody has to switch on. Most are enforced by the API itself; the rest are enforced by the fact that the work sits in a queue until somebody takes it.

Before a message goes

Every outreach task carries the exact text an agent intends to send. Open the thread, edit the payload, cancel the task, or claim it and send it yourself — a human and an agent claim through the same route.

When an agent got it wrong

Your correction is simply a later write, so it wins. The value the agent wrote stays on the timeline with its source and confidence beside it, which is how you find out whether that agent is worth trusting next week.

When someone asks to stop

Set the record to do_not_contact with the reason. Every later task on email, LinkedIn, WhatsApp or a call is refused. Research and review tasks still go through — you may think about them, you may not reach them.

When an agent stops mid-task

An owner or admin can release anything claimed, and everything chained behind it survives. Cancelling and recreating the task would break the thread, which is why release exists as its own route.

GET/v1/schema

What the workspace looks like afterwards

Read it back and this is what is there. None of it was configured in a settings screen: every object, field, stage and activity type in this list was written through the API by an agent, and every change is on the timeline as schema_changed with the diff attached.

/v1/schema
company
Keyed on the domain, lowercased and stripped of scheme and www.
person
Keyed on email or LinkedIn URL, linked to their company.
list
The campaign's candidates, each with a status and a reason.
deal
One per opportunity, on the default pipeline.
activities
  • enriched
  • field_changed
  • task_created
  • task_closed
  • replied
  • stage_changed
  • status_changed

Highlighted types are ones this workflow registers before it uses them.

channels
  • research
  • email

Out of the seven a task can carry.

pipeline
  • new
  • contacted
  • qualified
  • proposal
  • negotiation
  • won
  • lost

The pipeline every workspace starts with.

What this CRM does not do here

Said plainly, because each of these is a thing a CRM is usually expected to do, and an agent that assumes we do it will write code that never runs.

We do not find them

Search, scraping and data vendors are your agent's tools. This is where what it found gets written down, deduplicated and dated.

We do not enrich

No waterfall, no credits, no provider behind this one. A confidence on a field is your agent's judgement of its own source, and it is stored as such.

We never send

The task holds the words and, afterwards, the outcome. The mailbox is yours. There is no cadence engine either: a follow-up exists because an agent created one.

The other workflows

Same primitives, pointed somewhere else. The differences worth reading are in the schema each one asks for and the moment a person is called in.

GET/v1/schema

Agentic CRM

The category these three are instances of: what an agentic CRM is, how it differs from the one you already run, and why the provenance on every write is the part that matters.

Read the argument

GET/skill.md

The contract itself

The file an agent reads before it writes anything. Every workflow on this site is assembled out of the calls it documents, and nothing else.

Read /skill.md

Point an agent at an empty workspace

An agent can create one with no key, no invitation and no human, and run this whole workflow through it before anybody decides whether it belongs here. Your API calls are never rate limited and agents are never counted.