Create a CRM activity after a call
The smallest complete Talki integration: every finished call becomes an activity attached to a CRM person.
1. Subscribe
Section titled “1. Subscribe”Create a webhook endpoint for call.ended and call.deleted and save its
signing secret. Send a
ping from the Talki app to prove the route is reachable.
2. Accept safely
Section titled “2. Accept safely”On every request: capture the raw body, verify
X-Talki-Signature, insert the envelope id
under a unique constraint, and return 204 once the event is durably queued. An
id you have already seen is a redelivery — answer 204 and do nothing else.
3. Resolve the person
Section titled “3. Resolve the person”Read the Call from data.object. Prefer caller.contact_id once you have
mapped it; on first encounter, match caller.phone_number in E.164. A withheld
number is null, so file an unmatched activity rather than invent an identity.
caller.display_name holds only a name the owner curated or imported, and is
legitimately null much of the time.
4. Upsert the activity
Section titled “4. Upsert the activity”Key the activity on the Talki Call id. Store the summary, the timing, the
caller mapping and whichever appointment_ids your CRM needs, and upsert, so
redelivery and REST recovery repair the same record instead of adding one.
On call.deleted, delete the activity, and delete the tasks (step 6) whose
request IDs are in data.deleted.unresolved_request_ids: the event is the only
trace a deleted call leaves, and its unresolved requests are deleted with it
without an event of their own.
5. Recover missed calls
Section titled “5. Recover missed calls”Backfill once with GET /v1/calls?sync=true&limit=100. For every page:
- Upsert each Call by its Talki ID.
- Store
next_cursorwhenever it is non-null, including on the page wherehas_morebecomes false. - Continue immediately while
has_moreis true.
On startup and on a schedule, send the stored cursor back with sync=true and
repeat. The cursor is a commit-ordered position, so there is no overlap window to
tune. Keep the webhook path running for latency; the feed is what makes the copy
complete after downtime.
6. Turn open questions into tasks
Section titled “6. Turn open questions into tasks”A call where the assistant could not answer a question leaves an unresolved request: a question the assistant recorded, a number to call back, and a status that closes. Walk it with the same loop, on its own cursor:
curl --fail-with-body 'https://api.talki.it/v1/unresolved_requests?limit=100' \ -H "Authorization: Bearer $TALKI_API_KEY"Create a task keyed on the Talki request ID, attached to the person you resolved
in step 3, and link it to call_id so task and activity point at each other.
Sync without the status filter: a request that moves to resolved or
dismissed comes back through the feed, and that transition is what closes your
task. Needs unresolved:read.
When your users close the task with an answer every caller should hear, you can
also teach it to the assistant,
with knowledge:write.
