Common flows
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:
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:
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:
while :; do sleep 10 STATUS=$(auth "$API/tasks/$TASK" | jq -r .data.status) echo "$STATUS" case "$STATUS" in todo | in_progress) ;; *) break ;; esacdoneThe status that ends the loop tells you what to do next:
needs_you: answer each ask withPOST /asks/{id}, then run the loop again.in_review: read the work inactivity. Move the task todone, or comment to send it back.backlogorcanceled: nobody works on it.
Send the task back with a comment, or close it:
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:
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:
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:
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:
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:
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; doneThen search what the brain knows, by keyword and by meaning at once:
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:
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:
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.
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:
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:
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:
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:
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'