SMS Reminder API Documentation
    Developer
    Updated 15/04/202610 min read

    SMS Reminder API Documentation

    Complete API reference for adding contacts and managing SMS reminder campaigns programmatically.

    Quick Start

    Get started with the Remindlo API in 3 steps:

    1. Get your API key from Dashboard → Settings → API Keys

    2. List your campaigns to get campaign IDs

    3. Add contacts and enroll them in campaigns

    Optionally, set up webhooks to receive real-time notifications when data changes.

    # Example: Add a contact
    curl -X POST "https://api.remindlo.co.uk/v1/contacts" \
      -H "x-api-key: sk_live_your_key_here" \
      -H "Content-Type: application/json" \
      -d '{
        "phone": "+447912345678",
        "first_name": "John",
        "marketing_consent": true
      }'

    Authentication

    All API requests require an API key passed in the x-api-key header:

    x-api-key: sk_live_your_key_here

    Important: Keep your API key secure. Never expose it in client-side code or public repositories.

    Base URL

    https://api.remindlo.co.uk/v1

    Endpoints

    GET /v1/campaigns

    List all available SMS campaigns for your account.

    curl -X GET "https://api.remindlo.co.uk/v1/campaigns" \
      -H "x-api-key: sk_live_xxx"

    Response:

    {
      "campaigns": [
        {
          "id": "uuid",
          "name": "Appointment Reminder",
          "type": "recurring",
          "status": "running"
        }
      ]
    }

    POST /v1/contacts

    Create or update a contact. If a contact with the same phone or email exists, it will be updated.

    Request Body

    Field

    Type

    Required

    Description

    phone

    string

    *

    Phone in E.164 format (e.g., +447912345678)

    email

    string

    *

    Email address

    first_name

    string

    Contact's first name

    last_name

    string

    Contact's last name

    marketing_consent

    boolean

    Whether contact agreed to receive messages

    next_due_at

    string

    Next appointment date (ISO 8601)

    last_service_at

    string

    Last service date (ISO 8601)

    note

    string

    Notes about the contact

    tags

    string[]

    Tags for categorization

    custom_fields

    object

    Custom data as JSON

    campaign_ids

    string[]

    Campaign IDs to auto-enroll

    is_recurrent

    boolean

    Mark contact as a recurrent service — next_due_at automatically advances by the interval each time the service date passes

    recurrent_interval_value

    number

    Interval amount (1–999). Required when is_recurrent is true

    recurrent_interval_unit

    string

    days, months, or years. Required when is_recurrent is true

    * At least one of phone or email is required.

    curl -X POST "https://api.remindlo.co.uk/v1/contacts" \
      -H "x-api-key: sk_live_xxx" \
      -H "Content-Type: application/json" \
      -d '{
        "phone": "+447912345678",
        "first_name": "John",
        "last_name": "Smith",
        "marketing_consent": true,
        "next_due_at": "2025-03-15",
        "campaign_ids": ["campaign-uuid"],
        "is_recurrent": true,
        "recurrent_interval_value": 12,
        "recurrent_interval_unit": "months"
      }'

    Response:

    {
      "success": true,
      "contact_id": "uuid",
      "action": "created",
      "contact": {
        "id": "uuid",
        "first_name": "John",
        "last_name": "Smith",
        "phone": "+447912345678",
        "marketing_consent": true,
        "next_due_at": "2025-03-15",
        "is_recurrent": true,
        "recurrent_interval_value": 12,
        "recurrent_interval_unit": "months"
      },
      "enrollments": [
        { "campaign_id": "campaign-uuid", "status": "enrolled" }
      ]
    }

    POST /v1/campaigns-enroll

    Enroll an existing contact in a campaign. For one-off campaigns that are running, the message is queued immediately. For recurring campaigns, the scheduler handles sending.

    Request Body

    Field

    Type

    Required

    Description

    campaign_id

    string

    Yes

    UUID of the campaign

    contact_id

    string

    Yes*

    UUID of the contact to enroll (preferred)

    customer_id

    string

    Yes*

    Legacy alias for contact_id — still accepted for backwards compatibility

    * Provide either contact_id (recommended) or customer_id. If both are provided, contact_id takes precedence.

    curl -X POST "https://api.remindlo.co.uk/v1/campaigns-enroll" \
      -H "x-api-key: sk_live_xxx" \
      -H "Content-Type: application/json" \
      -d '{
        "campaign_id": "campaign-uuid",
        "contact_id": "contact-uuid"
      }'

    Response:

    {
      "enrollment_id": "uuid",
      "status": "enrolled"
    }

    Possible status values:

    • enrolled — contact successfully enrolled

    • already_enrolled — contact was already in this campaign

    • skipped — enrollment skipped (e.g., contact has no phone, or campaign is not active)

    POST /v1/messages

    Send a one-time SMS to an existing contact. Requires a paid plan. The message is queued immediately and sent via the configured SMS provider.

    Request Body

    Field

    Type

    Required

    Description

    contact_id

    string

    Yes

    UUID of the contact to message

    body

    string

    Yes

    Message text (max 1600 characters)

    channel

    string

    No

    Delivery channel. Default: sms

    curl -X POST "https://api.remindlo.co.uk/v1/messages" \
      -H "x-api-key: sk_live_xxx" \
      -H "Content-Type: application/json" \
      -d '{
        "contact_id": "contact-uuid",
        "body": "Hi John, just a quick reminder about your appointment tomorrow at 2pm."
      }'

    Response (202 Accepted):

    {
      "success": true,
      "message_id": "uuid",
      "status": "queued",
      "contact_id": "contact-uuid",
      "parts": 1,
      "channel": "sms"
    }

    Error responses:

    • 403 PLAN_REQUIRED — sending one-time messages requires a paid plan

    • 404 CONTACT_NOT_FOUND — contact does not exist or has no phone number

    • 429 SMS_LIMIT_EXCEEDED — SMS quota has been exceeded

    GET /v1/contacts

    List contacts with filtering and pagination.

    Query Parameters

    Parameter

    Type

    Description

    limit

    number

    Max results (default 50, max 100)

    offset

    number

    Skip first N results

    search

    string

    Search in name, phone, email

    has_phone

    boolean

    Only contacts with phone

    marketing_consent

    boolean

    Filter by consent

    next_due_before

    string

    Due date before (ISO 8601)

    next_due_after

    string

    Due date after (ISO 8601)

    sort_by

    string

    created_at, updated_at, next_due_at, first_name

    sort_order

    string

    asc or desc

    is_recurrent

    boolean

    Filter by recurrent status (true / false)

    created_after

    string

    Only contacts created after this date (ISO 8601)

    curl -X GET "https://api.remindlo.co.uk/v1/contacts?limit=10&search=john" \
      -H "x-api-key: sk_live_xxx"

    GET /v1/contacts/:id

    Get a single contact by ID, phone, or email.

    # By ID
    curl -X GET "https://api.remindlo.co.uk/v1/contacts/uuid" \
      -H "x-api-key: sk_live_xxx"
    
    # By phone
    curl -X GET "https://api.remindlo.co.uk/v1/contacts?phone=+447912345678" \
      -H "x-api-key: sk_live_xxx"

    Error Handling

    All errors return a consistent JSON format:

    {
      "success": false,
      "error": {
        "code": "INVALID_PHONE_FORMAT",
        "message": "Phone number must be in E.164 format",
        "details": {
          "field": "phone",
          "value": "07912345678",
          "suggestion": "Use format +447912345678 for UK numbers"
        }
      }
    }

    Error Codes

    HTTP

    Code

    Description

    400

    INVALID_PHONE_FORMAT

    Phone not in E.164 format

    400

    INVALID_EMAIL_FORMAT

    Invalid email address

    400

    MISSING_IDENTIFIER

    No phone or email provided

    401

    INVALID_API_KEY

    Missing or invalid API key

    401

    API_KEY_EXPIRED

    API key has expired

    404

    CONTACT_NOT_FOUND

    Contact does not exist

    404

    CAMPAIGN_NOT_FOUND

    Campaign does not exist

    500

    INTERNAL_ERROR

    Server error

    400

    MISSING_PHONE_NUMBER

    Contact has no phone number

    403

    PLAN_REQUIRED

    Feature requires a paid plan

    429

    SMS_LIMIT_EXCEEDED

    SMS quota exhausted

    Webhooks

    Receive real-time HTTP notifications when contacts, campaigns, or messages change. Create webhook endpoints programmatically or via the Dashboard.

    Event Types

    Event

    Triggered when

    contact.created

    A new contact is added

    contact.updated

    A contact's details change

    contact.deleted

    A contact is removed

    campaign.created

    A new campaign is created

    campaign.enrolled

    A contact is enrolled in a campaign

    message.sent

    An SMS is sent successfully

    message.failed

    An SMS delivery fails

    Webhook Payload

    Every webhook delivery sends a JSON envelope:

    {
      "id": "delivery-uuid",
      "type": "contact.created",
      "created_at": "2026-03-25T10:30:00Z",
      "api_version": "2026-03-25",
      "data": {
        "id": "contact-uuid",
        "first_name": "John",
        "last_name": "Smith",
        "phone_e164": "+447912345678",
        "email": null,
        "marketing_consent": true,
        "next_due_at": "2026-04-15T00:00:00Z",
        "tags": [],
        "created_at": "2026-03-25T10:30:00Z",
        "updated_at": "2026-03-25T10:30:00Z"
      }
    }

    Signature Verification

    Each delivery includes an X-Remindlo-Signature header for verifying authenticity:

    X-Remindlo-Signature: t=1711360200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8f9

    To verify, compute HMAC-SHA256 of {timestamp}.{raw_body} using your signing secret and compare with the v1 value.

    const crypto = require('crypto');
    
    function verifySignature(payload, header, secret) {
      const [tPart, vPart] = header.split(',');
      const timestamp = tPart.split('=')[1];
      const signature = vPart.split('=')[1];
      const expected = crypto
        .createHmac('sha256', secret)
        .update(timestamp + '.' + payload)
        .digest('hex');
      return crypto.timingSafeEqual(
        Buffer.from(signature, 'hex'),
        Buffer.from(expected, 'hex')
      );
    }

    GET /v1/webhooks

    List all webhook endpoints for your account.

    curl -X GET "https://api.remindlo.co.uk/v1/webhooks" \
      -H "x-api-key: sk_live_xxx"

    Response:

    {
      "success": true,
      "endpoints": [
        {
          "id": "endpoint-uuid",
          "name": "My CRM sync",
          "target_url": "https://example.com/webhook",
          "source": "manual",
          "status": "active",
          "event_types": ["contact.created", "contact.updated"],
          "created_at": "2026-03-25T10:00:00Z",
          "updated_at": "2026-03-25T10:00:00Z"
        }
      ]
    }

    POST /v1/webhooks

    Create a new webhook endpoint. Returns a signing secret that is shown only once - store it securely.

    Request Body

    Field

    Type

    Required

    Description

    name

    string

    Yes

    A label for this endpoint

    target_url

    string

    Yes

    HTTPS URL to receive events

    event_types

    string[]

    Yes

    Events to subscribe to (see table above)

    curl -X POST "https://api.remindlo.co.uk/v1/webhooks" \
      -H "x-api-key: sk_live_xxx" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "My CRM sync",
        "target_url": "https://example.com/webhook",
        "event_types": ["contact.created", "contact.updated", "contact.deleted"]
      }'

    Response:

    {
      "success": true,
      "endpoint": {
        "id": "endpoint-uuid",
        "name": "My CRM sync",
        "target_url": "https://example.com/webhook",
        "source": "manual",
        "status": "active",
        "event_types": ["contact.created", "contact.updated", "contact.deleted"]
      },
      "signing_secret": "whsec_a1b2c3d4e5f6..."
    }

    Important: The signing_secret is returned only once at creation time. Store it securely to verify incoming webhook signatures.

    POST /v1/webhooks/delete

    Delete a webhook endpoint and all its delivery history.

    Request Body

    Field

    Type

    Required

    Description

    endpoint_id

    string

    Yes

    The endpoint UUID to delete

    curl -X POST "https://api.remindlo.co.uk/v1/webhooks/delete" \
      -H "x-api-key: sk_live_xxx" \
      -H "Content-Type: application/json" \
      -d '{"endpoint_id": "endpoint-uuid"}'

    Response:

    {
      "success": true,
      "deleted": true
    }

    Retry Behaviour

    Failed deliveries are retried automatically with exponential backoff: 1 min, 5 min, 30 min, 2 hours, 6 hours, 12 hours, 24 hours (up to 8 attempts total). Endpoints with repeated permanent failures are automatically disabled. You can monitor delivery status and replay failed deliveries from the Webhooks dashboard.

    Use Cases

    AI Agent Integration

    AI agents (Claude, ChatGPT, custom agents) can manage your entire reminder workflow programmatically - adding contacts, enrolling in campaigns, and setting up webhooks to receive status updates.

    User: "Add John Smith, phone 07912345678, to the birthday campaign
           and set up a webhook so my CRM gets notified"
    AI: [calls GET /v1/campaigns to find "Birthday" campaign]
    AI: [calls POST /v1/contacts with campaign_ids]
    AI: [calls POST /v1/webhooks to create endpoint for contact events]
    AI: "Done! John is enrolled and your CRM will receive webhook
         notifications for any contact changes."

    CRM Integration

    Sync contacts from your CRM system automatically when appointments are booked. Use webhooks to push changes back to your CRM in real time. More use cases for using webhooks in a dedicated documentation.

    Zapier / Make / n8n

    Connect Remindlo to 8,000+ apps. Use our native Zapier integration or create webhook endpoints for Make and n8n.

    Code Examples

    JavaScript / Node.js

    const response = await fetch('https://api.remindlo.co.uk/v1/contacts', {
      method: 'POST',
      headers: {
        'x-api-key': 'sk_live_xxx',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        phone: '+447912345678',
        first_name: 'John',
        marketing_consent: true
      })
    });
    const data = await response.json();
    console.log(data.contact_id);

    Python

    import requests
    
    response = requests.post(
        'https://api.remindlo.co.uk/v1/contacts',
        headers={'x-api-key': 'sk_live_xxx'},
        json={
            'phone': '+447912345678',
            'first_name': 'John',
            'marketing_consent': True
        }
    )
    print(response.json()['contact_id'])

    PHP

    $ch = curl_init('https://api.remindlo.co.uk/v1/contacts');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'x-api-key: sk_live_xxx',
            'Content-Type: application/json'
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'phone' => '+447912345678',
            'first_name' => 'John',
            'marketing_consent' => true
        ])
    ]);
    $response = json_decode(curl_exec($ch), true);
    echo $response['contact_id'];

    Webhook Handler (Node.js / Express)

    const express = require('express');
    const crypto = require('crypto');
    const app = express();
    
    app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
      const signature = req.headers['x-remindlo-signature'];
      const [tPart, vPart] = signature.split(',');
      const timestamp = tPart.split('=')[1];
      const sig = vPart.split('=')[1];
    
      const expected = crypto
        .createHmac('sha256', process.env.WEBHOOK_SECRET)
        .update(timestamp + '.' + req.body)
        .digest('hex');
    
      if (!crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'))) {
        return res.status(401).send('Invalid signature');
      }
    
      const event = JSON.parse(req.body);
      console.log(event.type, event.data);
    
      // Handle event...
      res.status(200).send('OK');
    });

    Need Help?

    Contact us at support@remindlo.co.uk for API support.