Skip to main content
TroopyDocs
Open Troopy
Troopy

Scripts for tasks, approvals and drafts, the brain, campaigns and routines, ready to paste.

Five flows that people build on Troopy, each complete enough to paste into a script. Each flow uses this start:

bash
read -rs TROOPY_API_KEY && export TROOPY_API_KEYexport API="https://troopy.ai/api/v1"auth() { curl -sS -H "Authorization: Bearer $TROOPY_API_KEY" "$@"; }

Each flow names the scopes that its key needs. A call that changes data sends an Idempotency-Key, so a retry after a timeout never does the work two times.

Drive a task to the end

Scopes: tasks:read and tasks:write.

Give the task to a teammate by its handle, or leave out assignee for the Chief of Staff:

bash
TASK=$(auth -X POST "$API/tasks" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"title": "Draft the autumn price list", "priority": "high"}' | jq -r .data.key)

Read the task until the teammate stops working on it:

bash
while :; do  sleep 10  STATUS=$(auth "$API/tasks/$TASK" | jq -r .data.status)  echo "$STATUS"  case "$STATUS" in todo | in_progress) ;; *) break ;; esacdone

The status that ends the loop tells you what to do next:

  • needs_you: answer each ask with POST /asks/{id}, then run the loop again.
  • in_review: read the work in activity. Move the task to done, or comment to send it back.
  • backlog or canceled: nobody works on it.

Send the task back with a comment, or close it:

bash
auth -X POST "$API/tasks/$TASK/comments" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"text": "Add the wholesale prices too."}' auth -X PATCH "$API/tasks/$TASK" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"status": "done"}'

Clear what waits on you

Scopes: agents:read and agents:write. To approve a call, the key also needs the scopes of that call.

An approval is a call of a teammate that waits for a yes. List the approvals that wait, with the reason of each:

bash
auth "$API/approvals?status=pending" | jq -r '.data[] | "\(.id)  \(.agent)  \(.label): \(.reason)"'

Approve one, or send deny. Approval runs the call one time, as the teammate that asked:

bash
auth -X POST "$API/approvals/$APPROVAL_ID" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"decision": "approve"}'

A draft is a post, an email or copy that a teammate wrote for you. Troopy never sends or publishes a draft. List the drafts that are ready, then mark one used after you send it, or discarded:

bash
auth "$API/drafts?status=ready" | jq -r '.data[] | "\(.id)  \(.kind)  \(.title)"' auth -X PATCH "$API/drafts/$DRAFT_ID" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"status": "used"}'

Teach the brain

Scopes: brain:read and brain:write.

Add a note or the text of a page. The brain can search it at once, and the same text sent again gives back the same source:

bash
auth -X POST "$API/brain/sources" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"kind": "note", "title": "Wholesale terms", "text": "Wholesale customers pay on 30-day terms."}'

Learn a whole website. The workspace's computer reads up to 25 pages, so it must be on. The call gives 202 at once with a statusUrl:

bash
STATUS_URL=$(auth -X POST "$API/brain/imports" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"url": "https://example.com/"}' | jq -r .data.statusUrl) until [ "$(auth "$STATUS_URL" | jq -r .data.status)" != running ]; do sleep 10; done

Then search what the brain knows, by keyword and by meaning at once:

bash
auth "$API/brain/search?q=payment+terms" | jq -r '.data[] | "\(.title): \(.snippet)"'

Fill a list and start a campaign

Scopes: crm:write, outreach:read and outreach:write.

Make a list of people, then add up to 1,000 rows in each call. The keys of a row are the ids of the columns:

bash
LIST_ID=$(auth -X POST "$API/lists" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"name": "Cafe owners in Leeds", "kind": "people"}' | jq -r .data.id) auth -X POST "$API/lists/$LIST_ID/rows" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"rows": [{"name": "Jane Doe", "email": "jane@corner-cafe.example", "company": "Corner Cafe"}]}'

Find the id of a mailbox with GET /mailboxes. Make the campaign. It starts as a draft, and nothing goes out:

bash
CAMPAIGN_ID=$(auth -X POST "$API/campaigns" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{    "name": "Leeds cafes, wholesale",    "list": "'"$LIST_ID"'",    "mailboxes": ["'"$MAILBOX_ID"'"],    "steps": [{"kind": "email", "wait": 0, "write": "ai", "brief": "We bake sourdough for cafes. Offer a free tasting box.", "subject": null, "body": null}]  }' | jq -r .data.id)

Enroll the people of the list, and start the campaign. Enroll takes only emails that a check found good. Add "risky": true to also take emails that no check found good. Nobody on the do-not-contact list gets an email.

bash
auth -X POST "$API/campaigns/$CAMPAIGN_ID/enroll" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"all": true}' auth -X PATCH "$API/campaigns/$CAMPAIGN_ID" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"status": "active"}'

The Outreach teammate writes an email for each lead. Each email waits for a person. Read the drafts, then approve them:

bash
auth "$API/campaigns/$CAMPAIGN_ID/emails" | jq -r '.data[] | "\(.id)  \(.lead.email)  \(.subject)"' auth -X POST "$API/campaigns/$CAMPAIGN_ID/emails/decide" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"ids": ["'"$EMAIL_ID"'"], "decision": "approve"}'

An email that Troopy flagged needs its own approval. To start a campaign, the workspace needs its postal address and a mailbox with SPF or DKIM. See Send email campaigns.

Switch on a routine

Scopes: agents:read and agents:write. Only owners and admins switch on and run routines.

List the templates, and see which apps each one needs:

bash
auth "$API/templates" | jq -r '.data[] | select(.kind == "routine") | "\(.id)  \(.title)  \(.when)"'

Switch one on in your time zone. Troopy hires the teammate of its role when nobody has the role:

bash
ROUTINE_ID=$(auth -X POST "$API/templates/overdue-invoices/on" \  -H "Content-Type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"timezone": "Europe/Berlin"}' | jq -r .data.id)

Run it now to see what it does. The call gives 202 with the run, and statusUrl reads it:

bash
RUN_URL=$(auth -X POST "$API/routines/$ROUTINE_ID/run" -H "Idempotency-Key: $(uuidgen)" | jq -r .data.statusUrl) until auth "$RUN_URL" | jq -e '.data.finishedAt != null' > /dev/null; do sleep 10; doneauth "$RUN_URL" | jq -r '.data.summary'