careCycle
Campaign Enrollment

Campaign API guide

Add customers, restart campaign workflows, and set enrollment status using an API key.

Use campaign actions to connect your own trigger logic to careCycle. Customers can be identified by their careCycle customer ID or US phone number. Every action accepts one customer or a list of up to 1,000 input entries.

Authentication

Create a key in Developer Settings with the campaign_enrollment.manage capability. Include it in x-api-key. The same capability authorizes result polling. Add campaigns.view if your integration needs to discover campaign IDs through the campaigns API.

All campaign and customer references are scoped to the key's organization. Read-only playground keys cannot perform these actions.

Endpoints

MethodPathAction
POST/v2/campaigns/{campaignId}/enrollmentAdd customers
POST/v2/campaigns/{campaignId}/enrollment/restartRestart existing enrollments
POST/v2/campaigns/{campaignId}/enrollment/statusSet terminal enrollment status
GET/v2/campaigns/{campaignId}/enrollment/requests/{jobId}Read progress and results

Identify customers

Each reference must contain exactly one field:

{ "customerId": "223e4567-e89b-42d3-a456-426614174000" }
{ "phoneNumber": "+12025550123" }

US numbers such as 2025550123, 12025550123, and (202) 555-0123 are normalized to +12025550123. International numbers outside the +1 numbering format are not supported by this lookup.

Customers must already exist. These endpoints do not create customers or restore deleted records. A missing, deleted, or other-organization customer produces customer_not_found. Malformed customer entries fail individually with invalid_customer_reference and field-level validationErrors. Valid entries continue processing. For example, two malformed entries in a 1,000-entry list produce two item failures while the other 998 are processed normally.

The request still returns 202 with a job ID, even if all customer entries are invalid. Poll for results and correlate failures using each item's zero-based index. Request-level errors, such as an invalid campaign ID, providing both customer and customers, an empty or oversized list, or an unsupported terminal status, reject the whole request with 422.

Provide either customer or customers, never both. A list may mix IDs and phone numbers. If multiple references resolve to the same customer, its action runs once. Results retain every input position and identify duplicates with duplicateOf, the zero-based index of the first reference.

Add one customer

curl --request POST \
  "https://api.carecycle.ai/v2/campaigns/$CAMPAIGN_ID/enrollment" \
  --header "x-api-key: $CARECYCLE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "customer": { "phoneNumber": "+12025550123" }
  }'

Both individual and list requests return HTTP 202 Accepted with a job ID:

{
  "jobId": "123e4567-e89b-42d3-a456-426614174000",
  "statusUrl": "/v2/campaigns/423e4567-e89b-42d3-a456-426614174000/enrollment/requests/123e4567-e89b-42d3-a456-426614174000",
  "total": 1,
  "resolvedCount": 1
}

total counts input entries. resolvedCount counts unique existing customers that matched. Acceptance does not mean the workflow has executed or a call has been placed. Even requests where no customers match return a job ID with final per-item failures available through polling.

Change status for a list

POST to /v2/campaigns/{campaignId}/enrollment/status:

{
  "customers": [
    { "customerId": "223e4567-e89b-42d3-a456-426614174000" },
    { "phoneNumber": "+12025550124" }
  ],
  "status": "cancelled",
  "reason": "Outreach completed in our CRM"
}

Allowed statuses are completed, failed, and cancelled. reason is optional and limited to 255 characters. This changes the customer's enrollment, not the campaign itself or the customer's overall CRM status. Customers not enrolled in this campaign are skipped.

Setting a terminal status stops future campaign work and removes queued campaign events. It does not hang up a call already in progress or cancel separately scheduled appointments or inquiry callbacks.

Restart

POST to /v2/campaigns/{campaignId}/enrollment/restart:

{
  "customer": { "phoneNumber": "+12025550123" }
}

Restart requires an existing enrollment and a Pulse workflow. It reuses the enrollment record, clears workflow execution and failure state, and starts from the currently published workflow's entry node when the campaign is active. Customers not enrolled are skipped. Historical calls and enrollment history are preserved. Restart is not a full reset of customer data, call attempts, or appointment-booked information.

Adding a customer who is already active returns already_active. Adding someone whose enrollment has finished returns terminal_exists. Neither operation implicitly restarts them; use the restart endpoint explicitly.

Campaign restrictions

  • Active campaigns can execute newly enrolled or restarted customers.
  • Draft and paused campaigns accept enrollment, but workflow execution waits for the campaign to become active.
  • Completed, cancelled, archived, and deleted campaigns cannot receive new enrollments or restarts.
  • Receptionist campaign enrollments are managed automatically and cannot be changed through these endpoints.
  • A customer can have only one non-terminal retention journey at a time. A non-terminal enrollment in a draft or paused retention journey also occupies that slot. Retention notification campaigns are non-exclusive overlays.
  • There is one enrollment record per customer/campaign pair. Restart does not create a second simultaneous journey in the same campaign.
  • Required customer fields, workflow rules, calling hours, and call/SMS eligibility still apply. Enrollment does not bypass them.

Poll results

GET the returned statusUrl with the same API-key header. Poll every 2-5 seconds until status is completed, partial, or failed. Polls count toward the key's request limit.

{
  "jobId": "123e4567-e89b-42d3-a456-426614174000",
  "campaignId": "423e4567-e89b-42d3-a456-426614174000",
  "action": "enroll",
  "status": "completed",
  "total": 1,
  "processed": 1,
  "succeeded": 1,
  "skipped": 0,
  "failed": 0,
  "completedAt": "2026-10-01T17:00:00.000Z",
  "items": [
    {
      "index": 0,
      "customerId": "223e4567-e89b-42d3-a456-426614174000",
      "duplicateOf": null,
      "outcome": "succeeded",
      "code": "enrolled",
      "customerCampaignId": "523e4567-e89b-42d3-a456-426614174000"
    }
  ]
}

Results and counts refer to input positions, including duplicates. Items remain in input order. pending means an outcome is not available yet. A completed job can contain skips; always inspect item outcomes. Processing is per customer and is not an all-or-nothing transaction.

CodeMeaning
enrolledCreated an enrollment
restartedRestarted an existing enrollment
status_changedApplied the requested terminal status
already_activeEnrollment already active; no new enrollment
terminal_existsEnrollment already finished; explicit restart required
not_enrolledRestart/status skipped because no enrollment exists
customer_not_foundNo available customer in this organization
invalid_customer_referenceMalformed customer entry; inspect validationErrors for the field and explanation
blocked_active_retentionAnother non-terminal retention journey blocks this action
missing_required_fieldsEnrollment cannot proceed until required customer fields are populated
campaign_unavailableCampaign became unavailable or ineligible before processing
no_pulse_workflowNo workflow is available to restart
not_processedThe job failed before this item was attempted
execution_uncertainAn attempt was interrupted; review the enrollment before requesting another action

Results are persisted independently of the queue's 24-hour completed-job retention. Requests are scoped to both organization and campaign; inaccessible job IDs return 404.

Requests and optional retry protection

You do not need to generate an idempotency key to use these endpoints. careCycle creates the request identity and returns a job ID for polling. Without an idempotencyKey, each POST is a new request. A repeated add does not create another enrollment for the same customer/campaign pair; a repeated restart is a new restart action.

For integrations that automatically retry requests after timeouts, optionally include an idempotencyKey in the JSON body. An existing upstream event ID can serve as the key. The key is scoped to your organization across all campaign action endpoints. Reuse it for retries of one logical request, and use a new key for a new action.

{
  "customer": { "phoneNumber": "+12025550123" },
  "idempotencyKey": "upstream-event-123"
}
  • With a key, retry the same payload and key after a timeout. The original job ID and original resolved customer selection are reused.
  • Reusing the key with a different campaign, action, customer selection, status, or reason returns 409 IDEMPOTENCY_CONFLICT.
  • Completed items are not executed again when a job is replayed.
  • If a process fails after claiming an item but before recording its outcome, careCycle reports execution_uncertain instead of automatically restarting the customer again. Check the enrollment before issuing a new key.
  • To retry known failed items after correcting their data, send only those customers in a new request. If using retry keys, supply a new key for the corrected payload. Do not include already-successful customers in a new restart.
  • If a job remains queued because publication failed or its queue entry was lost, a keyed request can be republished by retrying its original POST and key.
  • Without a retry key, do not blindly retry a timed-out restart: it may have already been accepted. If you received a job ID, poll it; otherwise check the enrollment before initiating another restart.

Lists above 1,000 entries are rejected rather than truncated. Split larger sets into smaller batches and track each job separately. If using retry keys, give each batch its own key. Respect 429 responses and their retryAfter value.

On this page