Integrations API
External integrations
Send outbound messages and phone calls, or kick pre-built workflows from your CRM or automation platform. Authenticate with a project-scoped API key — no dashboard JWT required.
Authentication
Create an API key in Setup → Integrations → Developer API. Keys are project-scoped. Send the full secret once at creation as a Bearer token on every request.
Authorization: Bearer cp_live_<lookup>_<secret>
Responses use { success, data, error }. Production base URL:
https://copulse.app/api/v1/integrations/
API key scopes
| Scope | Allows |
|---|---|
outbound:message | POST outbound messages (WhatsApp, SMS, email, web) |
outbound:call | POST outbound phone calls |
outbound:workflow | POST workflow kickoffs (automation-first) |
triggers:read | Poll request status (also allowed with outbound scopes) |
Send an outbound message
curl -sS -X POST "https://copulse.app/api/v1/integrations/outbound/messages" \
-H "Authorization: Bearer cp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "hubspot-deal-991-whatsapp",
"channel": "whatsapp",
"to": { "phone": "+919876543210", "external_id": "hs-contact-42" },
"text": "Hi {{first_name}}, your site visit is confirmed.",
"contact": {
"name": "Priya Sharma",
"external_id": "hs-contact-42",
"properties": { "first_name": "Priya", "tier": "gold" }
},
"options": { "property_merge_mode": "fill_blank" }
}'
Response
{
"request_id": "uuid",
"event_id": "uuid",
"customer_id": "uuid",
"conversation_id": "uuid",
"status": "accepted"
}
Save request_id for status polling. Use template instead of text for WhatsApp template sends.
{{variable}} placeholders resolve from the trigger snapshot, CRM account properties, then contact columns.
Place an outbound call
curl -sS -X POST "https://copulse.app/api/v1/integrations/outbound/calls" \
-H "Authorization: Bearer cp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "hubspot-deal-991-call",
"to": { "phone": "+919876543210", "external_id": "hs-contact-42" },
"template": {
"id": "YOUR_PHONE_LIBRARY_TEMPLATE_ID",
"variables": ["Priya", "Saturday 11am"]
},
"contact": {
"name": "Priya Sharma",
"properties": { "first_name": "Priya", "project": "Skyline Towers" }
}
}'
Alternatively pass opening_message (plain text) instead of template. Wallet funds are reserved before the call is queued.
Trigger a workflow
curl -sS -X POST "https://copulse.app/api/v1/integrations/outbound/trigger" \
-H "Authorization: Bearer cp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"idempotency_key": "hubspot-deal-991-workflow",
"workflow_id": "YOUR_WORKFLOW_UUID",
"contact": {
"name": "Priya Sharma",
"external_id": "hs-contact-42",
"phone": "+919876543210",
"properties": { "first_name": "Priya" }
},
"properties": {
"deal_name": "Skyline Towers — Unit 4B",
"deal_stage": "site_visit_booked"
}
}'
Response
{
"request_id": "uuid",
"event_id": "uuid",
"customer_id": "uuid",
"workflow_id": "YOUR_WORKFLOW_UUID",
"status": "accepted"
}
CoPulse resolves the customer, merges properties into CRM account fields, stores an immutable trigger snapshot,
then publishes workflow.requested with payload.properties and payload.customer_id.
Poll request status
curl -sS "https://copulse.app/api/v1/integrations/outbound/requests/REQUEST_ID" \ -H "Authorization: Bearer cp_live_YOUR_KEY"
Returns the integration trigger row for your org/project, including:
status—accepted,processing,completed, orfailedevent_id,customer_id,conversation_idproperties— immutable snapshot from request timeresolved_preview— rendered text/template after{{…}}substitutiondelivery— delivery hint from Postgres + events index
Idempotency
Every request requires idempotency_key (max 200 chars), unique per project.
Retrying with the same key returns the original request_id and does not double-send.
- Generate a stable key from your source system (e.g.
hubspot-{dealId}-{action}). - On network timeout, retry the POST with the same body and key.
- Poll status until
delivery.stateis terminal.
Contact properties vs trigger snapshot
Send CRM fields in contact.properties. CoPulse merges them into the contact's
CRM account record (live state used by campaigns and agents).
The same merged values are copied to IntegrationTrigger.properties at request time —
an immutable snapshot for auditing. Later CRM edits do not change past trigger rows.
| Field | Where it lives | Mutable? |
|---|---|---|
contact.properties in API request | Input only | — |
CRM account custom_properties | Live contact/account | Yes (merge modes) |
IntegrationTrigger.properties | Per-request snapshot | No |
property_merge_mode: fill_blank (default) or overwrite on messages API.
HubSpot HTTP action setup
Trigger CoPulse when a HubSpot workflow enrolls a contact (e.g. deal stage = Won):
- In HubSpot, open Automation → Workflows and create or edit a contact-based workflow.
- Add an enrollment trigger (e.g. Deal stage is any of Won).
- Add action Send a webhook (or Custom code / HTTP if your tier uses it).
- Method: POST. URL:
https://copulse.app/api/v1/integrations/outbound/messages(or.../outbound/triggerfor workflow-first automation). - Headers:
Authorization: Bearer cp_live_…andContent-Type: application/json. - Map HubSpot tokens into
to.phone,contact.name, andcontact.properties. - Set
idempotency_keyto a HubSpot unique value, e.g.{{ contact.id }}-won-whatsapp. - Test with one contact. In CoPulse, open Setup → Integrations → Event Bus Activity to confirm delivery.