Docs sections

Welcome to Loriginal Closer API

Create an API key to get started.

Pagination and errors

Lists return 20 items by default. Set limit to 1–100. Pass pagination.next_cursor as after for the next page, keeping the same filters and limit. Stop when the cursor is null, even if a page is empty.

Successful responses use data. Errors include error.code and error.message explaining what failed.

List campaigns

#

Get your campaigns. Later, to add leads, you might need the campaign ids that this query returns.

GET/campaigns
curl --request GET \
  --url 'https://closer.loriginal.org/api/v1/campaigns' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY"
Example response200 OK
JSON
{
  "data": [
    {
      "id": "9bc96054-7b88-4bde-8c8a-1e3051f6778c",
      "name": "Follow-up",
      "status": "draft",
      "total_leads": 1,
      "created_at": "2026-09-07T14:00:00Z",
      "updated_at": "2026-09-07T14:00:00Z"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}

Add lead

#

Add a lead to an existing campaign. The only required field is phone.

POST/campaigns/{id}/leads

Replace {id} with a campaign ID. Use a phone number with its country code, such as +14165550123. If the campaign is running, the lead will be called within 1 minute if the pace is set to ASAP in the campaign settings; or sometime later during the day otherwise. If the campaign is stopped, the lead will be processed once the campaign starts.

If the lead already exists, their call cycle will be restarted, meaning they might get another call.

Optional fields
FieldDescription
first_name / last_nameContact name. Up to 100 characters each.
emailEmail address. Up to 254 characters.
companyCompany name. Up to 200 characters.
external_idYour CRM or contact ID. Up to 200 characters.
notes / otherExtra context for the agent. Up to 5,000 characters each.
languageLanguage code, such as en or fr_ca. New leads default to en.
verify_leadSet true for a paid Twilio phone lookup before adding. Defaults to false. Unreachable numbers are excluded from calls.
curl --request POST \
  --url 'https://closer.loriginal.org/api/v1/campaigns/9bc96054-7b88-4bde-8c8a-1e3051f6778c/leads' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "phone": "+14165550123",
  "first_name": "Alex",
  "external_id": "crm_123"
}'
Example response201 Created
JSON
{
  "data": {
    "id": "806e184b-94aa-46e9-83ec-c7d81735588b",
    "campaign_id": "9bc96054-7b88-4bde-8c8a-1e3051f6778c",
    "phone": "+14165550123",
    "first_name": "Alex",
    "external_id": "crm_123",
    "last_name": null,
    "full_name": "Alex",
    "email": null,
    "company": null,
    "notes": null,
    "other": null,
    "language": "en",
    "verification_status": "unverified",
    "verification_line_status": null,
    "verified_at": null,
    "excluded": false,
    "paused": false,
    "created_at": "2026-09-07T14:00:00Z",
    "updated_at": "2026-09-07T14:00:00Z"
  }
}

Find leads

#

Find contacts across your campaigns.

GET/leads
Filters
FieldDescription
external_idExact CRM or contact ID.
phoneExact phone number. Encode + as %2B.
emailEmail address, ignoring letter case.
campaign_idOnly leads in this campaign.
curl --request GET \
  --url 'https://closer.loriginal.org/api/v1/leads?external_id=crm_123' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY"
Example response200 OK
JSON
{
  "data": [
    {
      "id": "806e184b-94aa-46e9-83ec-c7d81735588b",
      "campaign_id": "9bc96054-7b88-4bde-8c8a-1e3051f6778c",
      "phone": "+14165550123",
      "first_name": "Alex",
      "external_id": "crm_123",
      "last_name": null,
      "full_name": "Alex",
      "email": null,
      "company": null,
      "notes": null,
      "other": null,
      "language": "en",
      "verification_status": "unverified",
      "verification_line_status": null,
      "verified_at": null,
      "excluded": false,
      "paused": false,
      "created_at": "2026-09-07T14:00:00Z",
      "updated_at": "2026-09-07T14:00:00Z"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}

Remove lead

#

Remove a lead from a campaign. The contact and call history are kept.

DELETE/campaigns/{id}/leads/{leadId}

Use a lead ID from Find leads. No body is needed. Queued or active calls may still finish.

curl --request DELETE \
  --url 'https://closer.loriginal.org/api/v1/campaigns/9bc96054-7b88-4bde-8c8a-1e3051f6778c/leads/806e184b-94aa-46e9-83ec-c7d81735588b' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY"
Example response200 OK
JSON
{
  "data": {
    "id": "806e184b-94aa-46e9-83ec-c7d81735588b",
    "campaign_id": null,
    "removed": true
  }
}

List calls

#

Read call history and results.

GET/calls

Use history_status for the result, such as Answered or No answer.

Filters
FieldDescription
campaign_idOnly this campaign's calls.
lead_idOnly this lead's history.
phoneSender or recipient number. Encode + as %2B.
date_from / date_toStart and end timestamps, such as 2026-09-07T00:00:00Z.
curl --request GET \
  --url 'https://closer.loriginal.org/api/v1/calls?campaign_id=9bc96054-7b88-4bde-8c8a-1e3051f6778c' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY"
Example response200 OK
JSON
{
  "data": [
    {
      "id": "b6f3d38e-86a8-457b-a09c-248c8e32ff63",
      "campaign_id": "9bc96054-7b88-4bde-8c8a-1e3051f6778c",
      "lead_id": "806e184b-94aa-46e9-83ec-c7d81735588b",
      "direction": "outbound",
      "from_number": "+14165550100",
      "to_number": "+14165550123",
      "status": "ended",
      "history_status": "Answered",
      "started_at": "2026-09-07T14:00:00Z",
      "ended_at": "2026-09-07T14:01:15Z",
      "duration_ms": 75000,
      "created_at": "2026-09-07T14:00:00Z",
      "updated_at": "2026-09-07T14:01:15Z",
      "recording_url": "https://closer.loriginal.org/api/v1/recordings/8d3a612a6f3748ff9b0a12c74b81e073609ecaa31c354f148ba3e08191b2f445"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}

Get call

#

Get a call's transcript, recording, and collected answers.

GET/calls/{id}

Use a call ID from List calls. Anyone with recording_url can play or download the audio.

curl --request GET \
  --url 'https://closer.loriginal.org/api/v1/calls/b6f3d38e-86a8-457b-a09c-248c8e32ff63' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY"
Example response200 OK
JSON
{
  "data": {
    "id": "b6f3d38e-86a8-457b-a09c-248c8e32ff63",
    "campaign_id": "9bc96054-7b88-4bde-8c8a-1e3051f6778c",
    "lead_id": "806e184b-94aa-46e9-83ec-c7d81735588b",
    "direction": "outbound",
    "from_number": "+14165550100",
    "to_number": "+14165550123",
    "status": "ended",
    "history_status": "Answered",
    "started_at": "2026-09-07T14:00:00Z",
    "ended_at": "2026-09-07T14:01:15Z",
    "duration_ms": 75000,
    "created_at": "2026-09-07T14:00:00Z",
    "updated_at": "2026-09-07T14:01:15Z",
    "recording_url": "https://closer.loriginal.org/api/v1/recordings/8d3a612a6f3748ff9b0a12c74b81e073609ecaa31c354f148ba3e08191b2f445",
    "transcript": "Call date and time: 2026-09-07T10:00:00-04:00 (ET)\nDuration: 75 seconds\nFrom: +14165550100 Sarah (Agent)\nTo: +14165550123 Alex (Lead)\nAgent: Hi Alex, is now a good time to talk?\nLead: Yes, go ahead.",
    "transcript_entries": [
      {
        "role": "agent",
        "content": "Hi Alex, is now a good time to talk?",
        "start": 0
      },
      {
        "role": "lead",
        "content": "Yes, go ahead.",
        "start": 3.2
      }
    ],
    "data_collected": {
      "interested": true
    },
    "disconnection_reason": "user_hangup"
  }
}

Send SMS

#

Send a text to a lead. A sending number is chosen depending on what number previously contacted the lead (SMS or phone call), or, which area code matches most with the lead's

POST/messages

Connect Twilio with an SMS number. Leads use their campaign's selected numbers. Opted-out contacts cannot receive messages.

FieldDescription
lead_id or to_numberA lead ID, or a phone number with its country code.
bodyMessage text, 1–1,600 characters.
Optional fields
FieldDescription
campaign_idUse this campaign's numbers. Defaults to the lead's campaign; must match when sending to a lead.
from_numberChoose one of your SMS numbers. It must be selected for the campaign when one is used.

A queued message is still waiting to send. Check message history for delivery. After a send error, check history before sending again.

curl --request POST \
  --url 'https://closer.loriginal.org/api/v1/messages' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "lead_id": "806e184b-94aa-46e9-83ec-c7d81735588b",
  "body": "Hi Alex, are you available tomorrow?"
}'
Example response201 Created
JSON
{
  "data": {
    "lead_id": "806e184b-94aa-46e9-83ec-c7d81735588b",
    "campaign_id": "9bc96054-7b88-4bde-8c8a-1e3051f6778c",
    "from_number": "+14165550100",
    "to_number": "+14165550123",
    "sender_selection": "call_history",
    "id": "SM8d3a612a6f3748ff9b0a12c74b81e073",
    "direction": "outbound",
    "body": "Hi Alex, are you available tomorrow?",
    "status": "queued",
    "occurred_at": "2026-09-07T14:00:00Z",
    "sent_at": null,
    "received_at": null
  }
}

List messages

#

Read message history from your connected Twilio account.

GET/messages

Filter by lead_id to get that contact's messages across campaigns.

Filters
FieldDescription
lead_idOnly this lead's history.
phoneSender or recipient number. Encode + as %2B.
date_from / date_toStart and end timestamps, such as 2026-09-07T00:00:00Z.
curl --request GET \
  --url 'https://closer.loriginal.org/api/v1/messages?lead_id=806e184b-94aa-46e9-83ec-c7d81735588b' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY"

With filters

curl --request GET \
  --url 'https://closer.loriginal.org/api/v1/messages?phone=%2B14165550123&date_from=2026-09-07T00:00:00Z&date_to=2026-09-07T23:59:59.999Z' \
  --header "Authorization: Bearer $LORIGINAL_CLOSER_API_KEY"
Example response200 OK
JSON
{
  "data": [
    {
      "id": "SM8d3a612a6f3748ff9b0a12c74b81e073",
      "lead_id": "806e184b-94aa-46e9-83ec-c7d81735588b",
      "lead_ids": [
        "806e184b-94aa-46e9-83ec-c7d81735588b"
      ],
      "direction": "outbound",
      "from_number": "+14165550100",
      "to_number": "+14165550123",
      "body": "Hi Alex, are you available tomorrow?",
      "occurred_at": "2026-09-07T14:00:00Z",
      "sent_at": "2026-09-07T14:00:00Z",
      "received_at": null,
      "status": "delivered"
    }
  ],
  "pagination": {
    "has_more": false,
    "next_cursor": null
  }
}