CoPulse API Reference

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

ScopeAllows
outbound:messagePOST outbound messages (WhatsApp, SMS, email, web)
outbound:callPOST outbound phone calls
outbound:workflowPOST workflow kickoffs (automation-first)
triggers:readPoll request status (also allowed with outbound scopes)

Send an outbound message

POST /api/v1/integrations/outbound/messages

Scope outbound:message · Returns 202 Accepted

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

POST /api/v1/integrations/outbound/calls

Scope outbound:call · Returns 202 Accepted

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

POST /api/v1/integrations/outbound/trigger

Scope outbound:workflow · Returns 202 Accepted

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

GET /api/v1/integrations/outbound/requests/:requestId

Requires triggers:read or any outbound scope

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:

  • statusaccepted, processing, completed, or failed
  • event_id, customer_id, conversation_id
  • properties — immutable snapshot from request time
  • resolved_preview — rendered text/template after {{…}} substitution
  • delivery — 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.

  1. Generate a stable key from your source system (e.g. hubspot-{dealId}-{action}).
  2. On network timeout, retry the POST with the same body and key.
  3. Poll status until delivery.state is 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.

FieldWhere it livesMutable?
contact.properties in API requestInput only
CRM account custom_propertiesLive contact/accountYes (merge modes)
IntegrationTrigger.propertiesPer-request snapshotNo

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):

  1. In HubSpot, open Automation → Workflows and create or edit a contact-based workflow.
  2. Add an enrollment trigger (e.g. Deal stage is any of Won).
  3. Add action Send a webhook (or Custom code / HTTP if your tier uses it).
  4. Method: POST. URL: https://copulse.app/api/v1/integrations/outbound/messages (or .../outbound/trigger for workflow-first automation).
  5. Headers: Authorization: Bearer cp_live_… and Content-Type: application/json.
  6. Map HubSpot tokens into to.phone, contact.name, and contact.properties.
  7. Set idempotency_key to a HubSpot unique value, e.g. {{ contact.id }}-won-whatsapp.
  8. Test with one contact. In CoPulse, open Setup → Integrations → Event Bus Activity to confirm delivery.