Subtext API Reference (3.8.0)

Download OpenAPI specification:Download

Introduction

The Subtext API is organized around REST. Our API has predictable resource-oriented URLs, accepts form-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.

New Features

Full API Access for All Campaigns

As of version 3.8.0, the Subtext API is now completely open to both free and paid campaigns with all features available. Previously restricted endpoints and functionality are now accessible to all campaign types, enabling greater flexibility and integration possibilities.

Webhooks

Version 3 adds Webhook subscriptions to notify your application when specific events occur.

Universal Subscriber Management

Version 3 unlocks Subtext API features for all of your subscribers — whether created via API, List Import, Keyword Subscribe, Embed, or Sign Up Page. Using Version 3, you can now interact programmatically with all of your Subtext subscribers.

Publish, Schedule, and Draft Broadcasts

Version 3 allows you to publish broadcasts to all of your subscribers or a targeted audience segment based on your subscribers' tags. This powerful feature represents an important milestone on the path to campaigns that are completely managed and/or automated via the Subtext API.

List, Search, and Create Tags

Version 3 allows you to list your campaign's existing tags, search for them by name, and create new tags. Better still, the returned tags include their active subscribers count. This feature is especially powerful when joined with the aforementioned targeted broadcasts functionality.

Subscriber Metadata

Version 3 allows you to take control of your data by attaching arbitrary key:value metadata objects to your subscribers. You can use this to store external identifiers or link data from a CRM, for example. Whatever your use-case, this keeps you in control of your most valuable Subtext data objects.

Tag Subscribers on Creation

Version 3 allows you to attach simple tags to your subscribers on creation. This allows for greater flexibility when using segmented broadcasts. It should also allow for some interesting new campaign engagement mechanisms.

List Messages for a Subscriber

Version 3 introduces the ability to list all messages from a conversation with a subscriber. This feature is available for both inbound and outbound messages.

Retrieve a Message

Version 3 introduces the ability to retrieve a message by its UUID. This feature is available for both inbound and outbound messages.

Update a Subscriber

Version 3 introduces the ability to update an individual subscriber's information including first_name, last_name, email, postal_code, note and metadata.

Add or Remove Tags from a Subscriber

Version 3 introduces the ability to add or remove tags from a subscriber. This feature is especially useful for managing subscriber segments and targeted broadcasts.

List reply Messages for a Broadcast

Version 3 introduces the ability to list all subscriber replies to a broadcast.

Breaking Changes

ExternalSubscriber Replaced by Subscriber

Version 3 does away with the ExternalSubscriber resource in favor of the underlying Subscriber resource. What this means in practice is that instead of using attributes of the ExternalSubscriber resource to make requests, you should use attributes of the Subscriber resource.

The Subscriber resource is source-agnostic. While it does keep track of the source of a subscription, it doesn't provide different responses (or functionality) based on the source. This makes interactions with the Subtext API much simpler and more deterministic.

Migrating from Earlier Versions

If you are migrating from Version 2 (or Version 1) of the Subtext API, your primary identifier for subscribers (subtext_uuid) still refers to the ExternalSubscriber resource and needs to be updated to refer to the Subscriber resource. To aid you in migrating your data, we have provided /v3/migrate endpoints that will help you map your existing subtext_uuid values to the v3-correct values. This is a simple operation that need only be run once in order to migrate your data.

Deprecations

While v1 and v2 of the Subtext API will remain fully functional, they are deprecated. We strongly recommend that all Subtext clients migrate to Version 3 as soon as possible.

The engagement response parameter returned for Broadcast resources will be renamed to response_rate in the next versioned release of the Subtext API. Clients relying on engagement should prepare to update their integrations accordingly.

Authentication

basicAuth

Basic HTTP authentication

The Subtext API uses your campaign's secret key to authenticate requests. You can view and manage your campaign's secret key on the Campaign tab of your Subtext Campaign Dashboard.

Be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.

Authentication to the API is performed via HTTP Basic Auth. Provide your campaign's secret key as the basic auth username value. The password should be left blank. Basic Auth requires you to send the username:password combination base64 encoded. So if your campaign's secret_key is AbexNAMtQptCb22Afzf62a, your Basic Auth Header will look like this:

Authorization: Basic QWJleE5BTXRRcHRDYjIyQWZ6ZjYyYTo=

QWJleE5BTXRRcHRDYjIyQWZ6ZjYyYTo= is the base64 encoded form of AbexNAMtQptCb22Afzf62a: (note the colon and blank password).

HTTPS is required

All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.

curl

In these docs, we use curl's -u option to pass the secret_key unencoded. curl performs the base64 encoding for you. For example:

 curl https://joinsubtext.com/v3/links \
  -u "AbexNAMtQptCb22Afzf62a:"


Security Scheme Type: HTTP
HTTP Authorization Scheme: basic

Subscriber

The Subscriber resource links your customer with their corresponding Subtext subscription.

The Subtext API's Subscriber endpoints enable you to do the following:

  • Subscribe your customer to a Subtext campaign.
  • List all of your customers' Subtext subscriptions.
  • Retrieve your customer's Subtext subscription.
  • Unsubscribe your customer from a Subtext campaign.
  • Resubscribe your customer to a Subtext campaign.
  • Update your customer's Subtext subscriber information.
  • Add or Remove Tags from your customer's Subtext subscription.
  • Retrieve a subscriber's shortened links by its UUID.
  • Convert a paid Stripe subscription to an external subscription.

Subscribe your customer to a Subtext campaign

Creates a Subscriber resource that is subscribed to the authenticated Campaign resource.

Authorizations:
basicAuth
Request Body schema: application/x-www-form-urlencoded
required
phone_number
required
string <phone>
Example: "+13025556789"

The subscriber's phone number (in E.164 format).

postal_code
string <zip>
Example: "10001"

Postal code of the subscriber in 5-digit or 9-digit format. This field is required for campaigns that require postal codes. Otherwise it is optional.

first_name
string
Example: "Jason"

First name of the subscriber.

last_name
string
Example: "Bourne"

Last name of the subscriber.

email
string <email>
Example: "david.webb@gmail.com"

Email address of the subscriber.

note
string
Example: "Some info about this subscriber."

Freeform text describing the subscriber.

tags
Array of strings
Example: "sports,Super Bowl"

An array of strings with which to tag the Subscriber.

metadata
object
Example: "[object Object]"

Set of key-value pairs that you can attach to a subscriber. This can be useful for storing additional information about the subscriber in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers \
  -u "a32f39fjas9dfan:" \
  --data-urlencode phone_number="+13025556789" \
  -d postal_code=10001 \
  -d first_name=Jason \
  -d last_name=Bourne \
  --data-urlencode email="david.webb@gmail.com" \
  --data-urlencode note="Some info about this subscriber." \
  -d "tags[]"=sports \
  --data-urlencode "tags[]"="Super Bowl" \
  -d "metadata[customer_id]"=ADQ-39A-02998 \
  -d "metadata[referral_id]"=b-299d-34da-zzor

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-03-22T18:56:57.989Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

List all of your customers' Subtext subscriptions

Retrieves all Subscriber resources belonging to the authenticated Campaign resource.

Authorizations:
basicAuth
query Parameters
page
integer
Example: page=1

Page number for paginating the response.

Responses

Request samples

curl -G https://joinsubtext.com/v3/subscribers \
  -u "a32f39fjas9dfan:" \
  -d page=1

Response samples

Content type
application/json
{
  • "pagination": {},
  • "subscribers": [
    • {
      • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
      • "created_at": "2022-03-22T18:56:57.989Z",
      • "updated_at": "2022-03-22T18:56:57.989Z",
      • "phone_number": "+13025556789",
      • "first_name": "Jason",
      • "last_name": "Bourne",
      • "email": "david.webb@gmail.com",
      • "note": "Some info about this subscriber.",
      • "postal_code": "10001",
      • "subscribed_at": "2022-03-22T18:56:58.810Z",
      • "unsubscribed_at": null,
      • "resubscribed_at": null,
      • "status": "Active",
      • "tags": [
        ],
      • "metadata": {
        }
      }
    ]
}

Retrieve your customer's Subtext subscription

Retrieves the Subscriber resource identified by the specified subtext_uuid.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to return.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab \
  -u "a32f39fjas9dfan:"

Response samples

Content type
application/json
Example
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-03-22T18:56:57.989Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Update Subscriber

Updates the Subscriber resource identified by the specified subtext_uuid with the provided parameters.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to update.

Request Body schema: application/x-www-form-urlencoded
required
postal_code
string <zip>
Example: "10001"

Postal code of the subscriber in 5-digit or 9-digit format.

first_name
string
Example: "Jason"

First name of the subscriber.

last_name
string
Example: "Bourne"

Last name of the subscriber.

email
string <email>
Example: "david.webb@gmail.com"

Email address of the subscriber.

note
string
Example: "Some info about this subscriber."

Freeform text describing the subscriber.

metadata
object
Example: "[object Object]"

Set of key-value pairs that you can attach to a subscriber. This can be useful for storing additional information about the subscriber in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab \
  -u "a32f39fjas9dfan:" \
  -d postal_code=10001 \
  -d first_name=Jason \
  -d last_name=Bourne \
  --data-urlencode email="david.webb@gmail.com" \
  --data-urlencode note="Some info about this subscriber." \
  -d "metadata[customer_id]"=ADQ-39A-02998 \
  -d "metadata[referral_id]"=b-299d-34da-zzor \
  -X PUT

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Retrieve your customer's Subtext subscription by phone number

Retrieves the Subscriber resource identified by the specified phone_number.

Authorizations:
basicAuth
path Parameters
phone_number
required
string <phone>
Example: +13025556789

Phone number (in E.164 format) of Subscriber to return.

Responses

Request samples

curl 'https://joinsubtext.com/v3/subscribers/+13025556789' \
  -u "a32f39fjas9dfan:"

Response samples

Content type
application/json
Example
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-03-22T18:56:57.989Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Update Subscriber by phone number

Updates the Subscriber resource identified by the specified phone_number with the provided parameters.

Authorizations:
basicAuth
path Parameters
phone_number
required
string <phone>
Example: +13025556789

Phone number (in E.164 format) of Subscriber to update.

Request Body schema: application/x-www-form-urlencoded
required
postal_code
string <zip>
Example: "10001"

Postal code of the subscriber in 5-digit or 9-digit format.

first_name
string
Example: "Jason"

First name of the subscriber.

last_name
string
Example: "Bourne"

Last name of the subscriber.

email
string <email>
Example: "david.webb@gmail.com"

Email address of the subscriber.

note
string
Example: "Some info about this subscriber."

Freeform text describing the subscriber.

metadata
object
Example: "[object Object]"

Set of key-value pairs that you can attach to a subscriber. This can be useful for storing additional information about the subscriber in a structured format. Individual keys can be unset by posting an empty value to them. All keys can be unset by posting an empty value to metadata.

Responses

Request samples

curl 'https://joinsubtext.com/v3/subscribers/+13025556789' \
  -u "a32f39fjas9dfan:" \
  -d postal_code=10001 \
  -d first_name=Jason \
  -d last_name=Bourne \
  --data-urlencode email="david.webb@gmail.com" \
  --data-urlencode note="Some info about this subscriber." \
  -d "metadata[customer_id]"=ADQ-39A-02998 \
  -d "metadata[referral_id]"=b-299d-34da-zzor \
  -X PUT

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Unsubscribe your customer from a Subtext campaign

Unsubscribes the Subscriber resource identified by the specified subtext_uuid.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to unsubscribe.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab/unsubscribe \
  -u "a32f39fjas9dfan:" \
  -X POST

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-08-03T05:11:21.410Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": "2022-08-03T05:11:21.410Z",
    • "resubscribed_at": null,
    • "status": "Unsubscribed",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Unsubscribe your customer from a Subtext campaign by phone number

Unsubscribes the Subscriber resource identified by the specified phone_number.

Authorizations:
basicAuth
path Parameters
phone_number
required
string <phone>
Example: +13025556789

Phone number (in E.164 format) of Subscriber to unsubscribe.

Responses

Request samples

curl 'https://joinsubtext.com/v3/subscribers/+13025556789/unsubscribe' \
  -u "a32f39fjas9dfan:" \
  -X POST

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-08-03T05:11:21.410Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": "2022-08-03T05:11:21.410Z",
    • "resubscribed_at": null,
    • "status": "Unsubscribed",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Resubscribe your customer to a Subtext campaign

Resubscribes the Subscriber resource identified by the specified subtext_uuid.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to resubscribe.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab/resubscribe \
  -u "a32f39fjas9dfan:" \
  -X POST

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": "2022-11-03T14:44:31.190Z",
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Resubscribe your customer to a Subtext campaign by phone number

Resubscribes the Subscriber resource identified by the specified phone_number.

Authorizations:
basicAuth
path Parameters
phone_number
required
string <phone>
Example: +13025556789

Phone number (in E.164 format) of Subscriber to resubscribe.

Responses

Request samples

curl 'https://joinsubtext.com/v3/subscribers/+13025556789/resubscribe' \
  -u "a32f39fjas9dfan:" \
  -X POST

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": "2022-11-03T14:44:31.190Z",
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Add a Tag to a Subscriber

Adds a Tag to the Subscriber resource identified by the specified subtext_uuid. You can provide either the Tag's subtext_uuid or name.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to add a tag to.

Request Body schema: application/x-www-form-urlencoded
required
object
subtext_uuid
string <uuid>
Example: "f3e95934-f412-46f2-9762-2936ade111bd"

The Subtext UUID of the tag to be added.

name
string
Example: "VIP Supporter"

The name of the tag to be added.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab/add_tag \
  -u "a32f39fjas9dfan:" \
  -d "tag[subtext_uuid]"=f3e95934-f412-46f2-9762-2936ade111bd \
  --data-urlencode "tag[name]"="VIP Supporter"

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "VIP Supporter"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Add a Tag to a Subscriber by phone number

Adds a Tag to the Subscriber resource identified by the specified phone_number. You can provide either the Tag's subtext_uuid or name.

Authorizations:
basicAuth
path Parameters
phone_number
required
string <phone>
Example: +13025556789

Phone number (in E.164 format) of Subscriber to add a tag to.

Request Body schema: application/x-www-form-urlencoded
required
object
subtext_uuid
string <uuid>
Example: "f3e95934-f412-46f2-9762-2936ade111bd"

The Subtext UUID of the tag to be added.

name
string
Example: "VIP Supporter"

The name of the tag to be added.

Responses

Request samples

curl 'https://joinsubtext.com/v3/subscribers/+13025556789/add_tag' \
  -u "a32f39fjas9dfan:" \
  -d "tag[subtext_uuid]"=f3e95934-f412-46f2-9762-2936ade111bd \
  --data-urlencode "tag[name]"="VIP Supporter"

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "VIP Supporter"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Remove a Tag from a Subscriber

Removes a Tag from the Subscriber resource identified by the specified subtext_uuid. You can provide either the Tag's subtext_uuid or name.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to remove a tag from.

Request Body schema: application/x-www-form-urlencoded
required
object
subtext_uuid
string <uuid>
Example: "f3e95934-f412-46f2-9762-2936ade111bd"

The Subtext UUID of the tag to be removed.

name
string
Example: "VIP Supporter"

The name of the tag to be removed.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab/remove_tag \
  -u "a32f39fjas9dfan:" \
  -d "tag[subtext_uuid]"=f3e95934-f412-46f2-9762-2936ade111bd \
  --data-urlencode "tag[name]"="VIP Supporter"

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Remove a Tag from a Subscriber by phone number

Removes a Tag from the Subscriber resource identified by the specified phone_number. You can provide either the Tag's subtext_uuid or name.

Authorizations:
basicAuth
path Parameters
phone_number
required
string <phone>
Example: +13025556789

Phone number (in E.164 format) of Subscriber to add a tag to.

Request Body schema: application/x-www-form-urlencoded
required
object
subtext_uuid
string <uuid>
Example: "f3e95934-f412-46f2-9762-2936ade111bd"

The Subtext UUID of the tag to be removed.

name
string
Example: "VIP Supporter"

The name of the tag to be removed.

Responses

Request samples

curl 'https://joinsubtext.com/v3/subscribers/+13025556789/remove_tag' \
  -u "a32f39fjas9dfan:" \
  -d "tag[subtext_uuid]"=f3e95934-f412-46f2-9762-2936ade111bd \
  --data-urlencode "tag[name]"="VIP Supporter"

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-11-03T14:44:31.190Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Convert a paid Stripe subscription to an external subscription

Converts an active paid Stripe subscription to an external (API-managed) subscription. This endpoint deactivates the Stripe subscription and removes Stripe-related metadata while keeping the Subscriber active. This is useful when transitioning payment management from Stripe to your own payment system.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to convert.

Responses

Request samples

curl https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab/convert_paid \
  -u "a32f39fjas9dfan:" \
  -X POST

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-03-22T18:56:57.989Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": null,
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [ ],
    • "metadata": null
    }
}

Convert a paid Stripe subscription to an external subscription by phone number

Converts an active paid Stripe subscription to an external (API-managed) subscription. This endpoint deactivates the Stripe subscription and removes Stripe-related metadata while keeping the Subscriber active. This is useful when transitioning payment management from Stripe to your own payment system.

Authorizations:
basicAuth
path Parameters
phone_number
required
string <phone>
Example: +13025556789

Phone number (in E.164 format) of Subscriber to convert.

Responses

Request samples

curl 'https://joinsubtext.com/v3/subscribers/+13025556789/convert_paid' \
  -u "a32f39fjas9dfan:" \
  -X POST

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-03-22T18:56:57.989Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": null,
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [ ],
    • "metadata": null
    }
}

Message

The Message resource represents an SMS or MMS message sent by a Subtext campaign.

The Subtext API's Message endpoints enable you to do the following:

  • Send an SMS or MMS to your customer.
  • List all messages to and from a subscriber.
  • Retrieve a message by its UUID.

Send an SMS or MMS Message

Creates a Message resource that is published to the specified Subscriber resource.

Authorizations:
basicAuth
Request Body schema: application/x-www-form-urlencoded
required
recipient_uuid
required
string <uuid>
Example: "61a8b662-e1a7-4457-ac08-baece296b4ab"

Unique identifier representing the specific subscriber who will receive the message.
This should be set to the subscriber's subtext_uuid.

body
required
string [ 1 .. 640 ] characters
Example: "Hey 👋 subscriber. Are you planning on watching the 🏀 game tonight?"

Text content of the message.
If the message has no text content (sending an image-only MMS, for example), this value can be set to an empty string.

attachment_urls
Array of strings <uri> [ items <uri > ]
Example: "https://picsum.photos/500/400.jpg,https://picsum.photos/seed/subtext/600/400.jpg"

An array of media URLs that will be attached to the message.
The files will be attached in the specified order.

shorten_links
boolean
Example: "false"

If set to true, all links in the body of the message will be shortened.
Links must be prepended with https:// for successful shortlink conversion.

Responses

Request samples

curl https://joinsubtext.com/v3/messages \
  -u "a32f39fjas9dfan:" \
  -d recipient_uuid=61a8b662-e1a7-4457-ac08-baece296b4ab \
  --data-urlencode body="Hey 👋 subscriber. Are you planning on watching the 🏀 game tonight?" \
  --data-urlencode "attachment_urls[]"="https://picsum.photos/500/400.jpg" \
  --data-urlencode "attachment_urls[]"="https://picsum.photos/seed/subtext/600/400.jpg" \
  --data-urlencode shorten_links="false"

Response samples

Content type
application/json
{
  • "message": {
    • "subscriber": {
      • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab"
      },
    • "subtext_uuid": "e3f78dd9-07ee-4c57-849f-dff34b7e079f",
    • "direction": "outbound",
    • "status": "queued",
    • "from": null,
    • "to": "+12125551234",
    • "body": "Hey 👋 subscriber. Are you planning on watching the 🏀 game tonight?",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "recipient_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab"
    }
}

Retrieve a Message

Retrieves a Message resource identified by the specified subtext_uuid.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: e3f78dd9-07ee-4c57-849f-dff34b7e079f

Subtext UUID of Message to return.

Responses

Request samples

curl https://joinsubtext.com/v3/messages/e3f78dd9-07ee-4c57-849f-dff34b7e079f \
  -u "a32f39fjas9dfan:"

Response samples

Content type
application/json
Example
{
  • "message": {
    • "subscriber": {
      • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab"
      },
    • "subtext_uuid": "e3f78dd9-07ee-4c57-849f-dff34b7e079f",
    • "direction": "inbound",
    • "status": "received",
    • "from": "+12125551234",
    • "to": "+14155552671",
    • "body": "Yes, I'm planning on watching the game tonight. Go Lakers!",
    • "attachment_urls": [ ],
    • "created_at": "2022-03-22T18:56:57.989Z"
    }
}

List all Messages to and from a Subscriber

Retrieves all Message resources associated with the specified Subscriber resource.
If a direction is provided, returns only those Message resources matching the direction.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 61a8b662-e1a7-4457-ac08-baece296b4ab

Subtext UUID of Subscriber to return messages for.

query Parameters
direction
string
Enum: "inbound" "outbound" "all"
Example: direction=all

Filter messages by direction.

limit
integer
Example: limit=20

Limit the number of messages per page.

page
integer
Example: page=1

Page number for paginating the response.

Responses

Request samples

curl -G https://joinsubtext.com/v3/subscribers/61a8b662-e1a7-4457-ac08-baece296b4ab/messages \
  -u "a32f39fjas9dfan:" \
  -d direction=all \
  -d limit=20 \
  -d page=1

Response samples

Content type
application/json
{}

Broadcast

The Broadcast resource represents a broadcast created by a Subtext campaign.

The Subtext API's Broadcast endpoints enable you to do the following:

  • List your campaign's published broadcasts and their metrics.
  • Publish a new broadcast to your subscribers.
  • Schedule a new broadcast.
  • Create a draft broadcast.
  • Retrieve a broadcast's shortened links by its UUID.
  • List all replies to a published broadcast.

List all of your campaign's published Broadcasts

Retrieves all published Broadcast resources belonging to the authenticated Campaign resource.

Authorizations:
basicAuth
query Parameters
page
integer
Example: page=1

Page number for paginating the response.

Responses

Request samples

curl -G https://joinsubtext.com/v3/broadcasts \
  -u "a32f39fjas9dfan:" \
  -d page=1

Response samples

Content type
application/json
{
  • "pagination": {},
  • "broadcasts": [
    • {
      • "subtext_uuid": "30e6fb89-034c-4ec8-bf62-87427da38854",
      • "status": "published",
      • "published_number": 149,
      • "publish_date": "2024-04-24",
      • "publish_time": "12:23:57 UTC",
      • "send_at": null,
      • "body": "Hey 👋 subscriber. Are you planning on watching the 🏀 game tonight?",
      • "attach_vcard": false,
      • "segment": {
        },
      • "engagement": "13.2%",
      • "recipients": 3294,
      • "replies_count": 435
      }
    ]
}

Create, Schedule, and/or Publish a new Broadcast

Creates a Broadcast resource that is either:

  • Saved as a draft.
  • Immediately published to the specified audience segment.
  • Scheduled for publication to the specified audience segment.
Authorizations:
basicAuth
Request Body schema: application/x-www-form-urlencoded
required
body
required
string [ 1 .. 640 ] characters
Example: "Hello awesome subscribers. What do you think of the new logo?"

Text content of the broadcast.

attachment_urls
Array of strings <uri> [ items <uri > ]
Example: "https://picsum.photos/500/400.jpg,https://picsum.photos/seed/subtext/600/400.jpg"

An array of media URLs that will be attached to the broadcast.
The files will be attached in the specified order.
Limit 32MB per file.

attach_vcard
boolean
Example: "false"

If set to true, the broadcast will be sent with the campaign's vCard attached.

object

Specify a subset of your audience to send the broadcast to.
If you want the broadcast to go to all of your subscribers, you can ignore this or specify all_subscribers as the audience value.

audience
required
string
Enum: "all_subscribers" "all_tags" "any_tags" "excluding_tags"
Example: "excluding_tags"

Controls how the specified segment tags will be used to filter the recipients of the broadcast.

tag_ids
Array of strings <uuid> [ items <uuid > ]
Example: "68a45670-3005-4186-acc0-09ee01b4e82d,5e566b8e-5425-4152-aa14-e17bb4e8d2a9"

An array of tag ids that will be used to filter the recipients of the broadcast according to the segment audience.
You can search the /v3/tags endpoint to retrieve tag ids.

send_at
string <date-time>
Example: "2025-05-04T15:30:00Z"

Date and time the broadcast will be published (in ISO 8601 format).
To ensure the broadcast is published at the correct time, please use UTC time.

shorten_links
boolean
Example: "false"

If set to true, all links in the body of the broadcast will be shortened.
Links must be prepended with https:// for successful shortlink conversion.

publish_now
boolean
Example: "false"

If set to true, the broadcast will be published immediately upon creation.

⚠️ IMPORTANT

  • If send_at is specified, publish_now will be ignored.
  • If send_at is not specified and publish_now is false, the broadcast will be saved as a draft.

Responses

Request samples

curl https://joinsubtext.com/v3/broadcasts \
  -u "a32f39fjas9dfan:" \
  --data-urlencode body="Hello awesome subscribers. What do you think of the new logo?" \
  --data-urlencode "attachment_urls[]"="https://picsum.photos/500/400.jpg" \
  --data-urlencode "attachment_urls[]"="https://picsum.photos/seed/subtext/600/400.jpg" \
  --data-urlencode attach_vcard="false" \
  -d "segment[audience]"=excluding_tags \
  -d "segment[tag_ids][]"=68a45670-3005-4186-acc0-09ee01b4e82d \
  -d "segment[tag_ids][]"=5e566b8e-5425-4152-aa14-e17bb4e8d2a9 \
  --data-urlencode send_at="2025-05-04T15:30:00Z" \
  --data-urlencode shorten_links="false" \
  --data-urlencode publish_now="false"

Response samples

Content type
application/json
Example
{
  • "broadcast": {
    • "subtext_uuid": "49083c84-82ae-4b09-b210-6b8f0a48d751",
    • "status": "scheduled",
    • "published_number": null,
    • "publish_date": null,
    • "publish_time": null,
    • "send_at": "2025-05-04T15:30:00Z",
    • "body": "Mother's Day is coming up. We've got our resident gift-giving expert here to answer your questions.",
    • "attachment_urls": [],
    • "attach_vcard": false,
    • "segment": {
      • "audience": "all_tags",
      • "tags": [
        ]
      },
    • "engagement": null,
    • "recipients": null,
    • "replies_count": null
    }
}

List all replies to a Broadcast

Retrieves a paginated list of reply Message resources associated with the specified Broadcast resource.

Authorizations:
basicAuth
path Parameters
subtext_uuid
required
string <uuid>
Example: 30e6fb89-034c-4ec8-bf62-87427da38854

Subtext UUID of Broadcast to return replies for.

query Parameters
limit
integer
Example: limit=20

Limit the number of replies per page.

page
integer
Example: page=1

Page number for paginating the response.

Responses

Request samples

curl -G https://joinsubtext.com/v3/broadcasts/30e6fb89-034c-4ec8-bf62-87427da38854/replies \
  -u "a32f39fjas9dfan:" \
  -d limit=20 \
  -d page=1

Response samples

Content type
application/json
{}

Tag

The Tag resource represents a tag that can be attached to subscribers of a Subtext campaign.

The Subtext API's Tag endpoints enable you to do the following:

  • List your campaign's tags.
  • Search your campaign's tags.
  • Create a new tag.

List or Search all of your campaign's Tags

Retrieves all Tag resources belonging to the authenticated Campaign resource.
If a query is provided, returns only Tag resources matching it.

Authorizations:
basicAuth
query Parameters
query
string
Examples:
  • query=import - Return only those tags containing the substring "import".

The search query used to filter the results.

page
integer
Example: page=1

Page number for paginating the response.

Responses

Request samples

curl -G https://joinsubtext.com/v3/tags \
  -u "a32f39fjas9dfan:" \
  -d query=import \
  -d page=1

Response samples

Content type
application/json
{
  • "pagination": {},
  • "tags": [
    • {
      • "subtext_uuid": "fb9af27f-2481-4684-8617-a2ea8d991bdf",
      • "name": "Imported via Instagram",
      • "active_subscribers_count": 351
      }
    ]
}

Create a Tag

Creates a Tag resource belonging to the authenticated Campaign resource.

Authorizations:
basicAuth
Request Body schema: application/x-www-form-urlencoded
required
name
required
string [ 1 .. 255 ] characters
Example: "VIP"

The name of the tag.

Responses

Request samples

curl https://joinsubtext.com/v3/tags \
  -u "a32f39fjas9dfan:" \
  -d name=VIP

Response samples

Content type
application/json
{
  • "tag": {
    • "subtext_uuid": "e3f78dd9-07ee-4c57-849f-dff34b7e079f",
    • "name": "VIP",
    • "active_subscribers_count": 0
    }
}

Migrate

The Subtext API's Migrate endpoints enable you to do the following:

  • List your campaign's ExternalSubscriber→Subscriber mappings.
  • Retrieve the Subscriber resource for a given ExternalSubscriber.

List all of your campaign's ExternalSubscriber→Subscriber mappings

Retrieves all ExternalSubscriber→Subscriber mappings belonging to the authenticated Campaign resource.

Authorizations:
basicAuth
query Parameters
page
integer
Example: page=1

Page number for paginating the response.

Responses

Request samples

curl -G https://joinsubtext.com/v3/migrate \
  -u "a32f39fjas9dfan:" \
  -d page=1

Response samples

Content type
application/json
{
  • "pagination": {},
  • "mappings": [
    • {
      • "v2_subtext_uuid": "a36cf5f3-5653-4307-a4f0-147b2f6d10c1",
      • "v3_subtext_uuid": "dd116ba1-f1e3-4298-bca0-b52953204d79"
      },
    • {
      • "v2_subtext_uuid": "77cd5f7b-3f39-4814-96ad-ac6bd3c52363",
      • "v3_subtext_uuid": "2efea4ec-67a6-45e2-a720-60d5ab248537"
      }
    ]
}

Retrieve the Subscriber resource for a given ExternalSubscriber

Retrieves the Subscriber resource associated with the specified ExternalSubscriber subtext_uuid.

Authorizations:
basicAuth
path Parameters
v2_subtext_uuid
required
string <uuid>
Example: 1f03d50c-f3d9-4cbe-ac30-6885e4e3114e

Subtext UUID of ExternalSubscriber whose associated Subscriber you wish to return.

Responses

Request samples

curl https://joinsubtext.com/v3/migrate/1f03d50c-f3d9-4cbe-ac30-6885e4e3114e \
  -u "a32f39fjas9dfan:"

Response samples

Content type
application/json
{
  • "subscriber": {
    • "subtext_uuid": "61a8b662-e1a7-4457-ac08-baece296b4ab",
    • "created_at": "2022-03-22T18:56:57.989Z",
    • "updated_at": "2022-03-22T18:56:57.989Z",
    • "phone_number": "+13025556789",
    • "first_name": "Jason",
    • "last_name": "Bourne",
    • "email": "david.webb@gmail.com",
    • "note": "Some info about this subscriber.",
    • "postal_code": "10001",
    • "subscribed_at": "2022-03-22T18:56:58.810Z",
    • "unsubscribed_at": null,
    • "resubscribed_at": null,
    • "status": "Active",
    • "tags": [
      • "sports",
      • "Super Bowl"
      ],
    • "metadata": {
      • "customer_id": "ADQ-39A-02998",
      • "referral_id": "b-299d-34da-zzor"
      }
    }
}

Webhooks

Overview

Webhooks are HTTP POST requests that Subtext sends to your application when specific events occur. They allow you to receive real-time notifications about subscriber activity, enabling you to build responsive integrations.

Available Events

Subtext supports the following subscribable events:

  • subscriber.message.received - Triggered when a subscriber sends a message to your campaign.
  • subscriber.subscribed - Triggered when a new subscriber joins your campaign.
  • subscriber.unsubscribed - Triggered when a subscriber leaves your campaign.
  • subscriber.resubscribed - Triggered when a previously unsubscribed subscriber rejoins your campaign.
  • shortlink.direct_message_clicked - Triggered when a subscriber clicks a shortlink in a direct message.
  • webhook_endpoint.edited - Triggered when a webhook endpoint configuration is modified.

Webhook Requirements

Response Requirements

Your webhook endpoint must:

  • Respond with a 200 HTTP status code within 5 seconds.
  • Accept HTTP POST requests over HTTPS.
  • Be publicly accessible (no localhost or private network addresses).

Any response outside the 200-299 range will be considered a failure and may result in retries.

Timeout Behavior

  • Webhook requests have a 5-second timeout.
  • Requests that exceed this timeout will be marked as failed.
  • Failed requests may be retried automatically depending on the failure type.

Signature Verification

⚠️ Important: Always verify webhook signatures to ensure requests are from Subtext and haven't been tampered with.

Why Verify Signatures

  • Security: Prevents malicious actors from sending fake webhook requests to your endpoint.
  • Integrity: Ensures the payload hasn't been modified in transit.
  • Authentication: Confirms the request actually came from Subtext.

How to Verify

  1. Get your signing secret: Available in your campaign's webhook endpoint configuration.
  2. Extract the signature: From the X-Subtext-Signature header.
  3. Compute the expected signature: Using HMAC-SHA256 with your signing secret and the raw request body.
  4. Compare signatures: Use a secure comparison method to verify they match.

Implementation Examples

Node.js / JavaScript

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}
// Usage in Express.js app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-subtext-signature'];
  const payload = req.body;
  const secret = process.env.SUBTEXT_WEBHOOK_SECRET;

  if (!verifyWebhookSignature(payload, signature, secret)) {
    return res.status(401).send('Unauthorized');
  }

  // Process the webhook...
  res.status(200).send('OK');
});

Python

def verify_webhook_signature(payload, signature, secret):
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected_signature)

# Usage in Flask from flask import Flask, request
@app.route('/webhook', methods=['POST']) def webhook():
    signature = request.headers.get('X-Subtext-Signature')
    payload = request.get_data(as_text=True)
    secret = os.environ['SUBTEXT_WEBHOOK_SECRET']

    if not verify_webhook_signature(payload, signature, secret):
        return 'Unauthorized', 401

    # Process the webhook...
    return 'OK', 200

Ruby

def verify_webhook_signature(payload, signature, secret)
  expected_signature = OpenSSL::HMAC.hexdigest('SHA256', secret, payload)
  Rack::Utils.secure_compare(signature, expected_signature)
end
# Usage in Rails def webhook
  signature = request.headers['X-Subtext-Signature']
  payload = request.raw_post
  secret = ENV['SUBTEXT_WEBHOOK_SECRET']

  unless verify_webhook_signature(payload, signature, secret)
    render plain: 'Unauthorized', status: :unauthorized
    return
  end

  # Process the webhook...
  render plain: 'OK', status: :ok
end

Important Notes

  • Use the raw request body: Don't parse the JSON before verifying the signature.
  • Secure comparison: Always use timing-safe comparison functions to prevent timing attacks.
  • Store secrets securely: Keep your signing secret in environment variables or secure configuration.
  • Verify every request: Never skip signature verification, even in development.

Payload Format

All webhook payloads follow a consistent JSON structure. The event_detail object varies based on the event type.

Request Headers

Each webhook request includes the following headers:

Header Description
User-Agent Always set to Subtext-HooksAgent
Content-Type Always set to application/json
X-Subtext-Signature HMAC-SHA256 signature for payload verification
X-Subtext-Hook-ID Unique identifier for the webhook endpoint

Error Handling and Retries

Response Codes

  • 200-299: Success - webhook processed successfully.
  • 400-499: Client error - typically indicates a configuration issue.
  • 500-599: Server error - temporary issue, may be retried.
  • Timeout: Request took longer than 5 seconds.

Retry Behavior

Subtext automatically retries failed webhook deliveries with exponential backoff:

  • Network errors and timeouts are retried.
  • Server errors (5xx) are retried.
  • Rate limiting (429) is retried.
  • Client errors (4xx) are retried.

Best Practices

  1. Always verify signatures - This cannot be emphasized enough.
  2. Respond quickly - Aim for responses well under the 5-second timeout.
  3. Handle idempotency - The same event might be delivered multiple times.
  4. Use HTTPS - Required for all webhook endpoints.
  5. Log webhook activity - Helps with debugging and monitoring.
  6. Handle failures gracefully - Temporary failures shouldn't break your application.
  7. Monitor your endpoints - Set up alerts for webhook delivery failures.

Testing

During development, you can use tools like ngrok or webhook.site to receive webhooks.

Subscriber Message Received Webhook

Triggered when your Subtext campaign receives an inbound SMS or MMS message from a subscriber. This includes replies to broadcasts, keyword subscriptions, and general subscriber communications.

Authorizations:
basicAuth
Request Body schema: application/json
event_type
string
Enum: "subscriber.message.received" "subscriber.subscribed" "subscriber.unsubscribed" "subscriber.resubscribed" "shortlink.direct_message_clicked" "webhook_endpoint.edited"

The type of event that triggered this webhook.

object

Information about the owner of the webhook endpoint.

owner_type
string
Value: "campaign"

Always "campaign" for current implementation.

subtext_uuid
string

Unique identifier for the campaign.

object

Event-specific data payload.

object
subtext_uuid
string

UUID of the subscriber who sent the message.

object
subtext_uuid
string

UUID of the received message.

created_at
string <date-time>

Timestamp when the message was created.

direction
string
Value: "inbound"

Message direction (always inbound for received messages).

status
string
Value: "received"

Message status.

from
string

Sender's phone number.

to
string

Recipient's phone number (campaign number).

body
string

Content of the received message.

null or object

The message object this is replying to (null if not a reply).

One of
null

The message object this is replying to (null if not a reply).

Responses

Request samples

Content type
application/json
{
  • "event_type": "subscriber.message.received",
  • "event_owner": {
    • "owner_type": "campaign",
    • "subtext_uuid": "cb33aea2-3554-4834-9dec-f03d8f79f22a"
    },
  • "event_detail": {
    • "subscriber": {
      • "subtext_uuid": "37d7e67f-7a49-4dd5-aff6-4398613ba599"
      },
    • "message": {
      • "subtext_uuid": "49d83cef-e983-45cc-8bcb-6d2458e02341",
      • "created_at": "2025-11-06T16:39:54Z",
      • "direction": "inbound",
      • "status": "received",
      • "from": "+14155551234",
      • "to": "+13464964807",
      • "body": "Hello",
      • "in_reply_to": {
        }
      }
    }
}

Subscriber Subscribed Webhook

Triggered when a new subscriber is added to your campaign through any method (API, keyword subscribe, embed form, sign-up page, or list import).

Authorizations:
basicAuth
Request Body schema: application/json
event_type
string
Enum: "subscriber.message.received" "subscriber.subscribed" "subscriber.unsubscribed" "subscriber.resubscribed" "shortlink.direct_message_clicked" "webhook_endpoint.edited"

The type of event that triggered this webhook.

object

Information about the owner of the webhook endpoint.

owner_type
string
Value: "campaign"

Always "campaign" for current implementation.

subtext_uuid
string

Unique identifier for the campaign.

object

Event-specific data payload.

object
subtext_uuid
string

UUID of the subscriber created.

phone_number
string

Phone number of the subscriber created.

first_name
string

First name of the subscriber.

last_name
string

Last name of the subscriber.

email
string

Email address of the subscriber.

postal_code
string

Postal code of the subscriber.

city
string

City where subscriber is located.

state
string

State where subscriber is located.

null or object

Additional metadata for the subscriber.

One of
null

Additional metadata for the subscriber.

signup_method
string

How the subscriber was added to the campaign.

subscribed_at
string <date-time>

When the subscriber first subscribed (ISO 8601 format).

Responses

Request samples

Content type
application/json
{
  • "event_type": "subscriber.subscribed",
  • "event_owner": {
    • "owner_type": "campaign",
    • "subtext_uuid": "cb33aea2-3554-4834-9dec-f03d8f79f22a"
    },
  • "event_detail": {
    • "subscriber": {
      • "subtext_uuid": "32d83ce1-e983-44cc-8bcb-6d1458e05617",
      • "phone_number": "+15551234567",
      • "first_name": "John",
      • "last_name": "Doe",
      • "email": "john.doe@example.com",
      • "postal_code": "10001",
      • "city": "New York",
      • "state": "NY",
      • "metadata": {
        },
      • "signup_method": "campaign_page",
      • "subscribed_at": "2023-11-01T10:00:00Z"
      }
    }
}

Subscriber Unsubscribed Webhook

Triggered when a subscriber is removed from your campaign through any method (API, STOP keyword, compliance, or manual removal).

Authorizations:
basicAuth
Request Body schema: application/json
event_type
string
Enum: "subscriber.message.received" "subscriber.subscribed" "subscriber.unsubscribed" "subscriber.resubscribed" "shortlink.direct_message_clicked" "webhook_endpoint.edited"

The type of event that triggered this webhook.

object

Information about the owner of the webhook endpoint.

owner_type
string
Value: "campaign"

Always "campaign" for current implementation.

subtext_uuid
string

Unique identifier for the campaign.

object

Event-specific data payload.

object
subtext_uuid
string

UUID of the subscriber.

phone_number
string

Phone number of the subscriber.

first_name
string

First name of the subscriber.

last_name
string

Last name of the subscriber.

email
string

Email address of the subscriber.

postal_code
string

Postal code of the subscriber.

city
string

City where subscriber is located.

state
string

State where subscriber is located.

metadata
object

Additional metadata for the subscriber.

signup_method
string

How the subscriber was added to the campaign.

sms_opt_out
boolean

Whether the subscriber has opted out of SMS.

subscribed_at
string <date-time>

When the subscriber first subscribed (ISO 8601 format).

resubscribed_at
string <date-time>

When the subscriber last resubscribed (ISO 8601 format).

unsubscribed_at
string <date-time>

When the subscriber unsubscribed (ISO 8601 format).

Responses

Request samples

Content type
application/json
{
  • "event_type": "subscriber.unsubscribed",
  • "event_owner": {
    • "owner_type": "campaign",
    • "subtext_uuid": "cb33aea2-3554-4834-9dec-f03d8f79f22a"
    },
  • "event_detail": {
    • "subscriber": {
      • "subtext_uuid": "32d83ce1-e983-44cc-8bcb-6d1458e05617",
      • "phone_number": "+15551234567",
      • "first_name": "John",
      • "last_name": "Doe",
      • "email": "john.doe@example.com",
      • "postal_code": "10001",
      • "city": "New York",
      • "state": "NY",
      • "metadata": {
        },
      • "signup_method": "campaign_page",
      • "sms_opt_out": true,
      • "subscribed_at": "2023-11-01T10:00:00Z",
      • "resubscribed_at": null,
      • "unsubscribed_at": "2023-11-07T16:45:00Z"
      }
    }
}

Subscriber Resubscribed Webhook

Triggered when a previously unsubscribed subscriber rejoins your campaign through any reactivation method.

Authorizations:
basicAuth
Request Body schema: application/json
event_type
string
Enum: "subscriber.message.received" "subscriber.subscribed" "subscriber.unsubscribed" "subscriber.resubscribed" "shortlink.direct_message_clicked" "webhook_endpoint.edited"

The type of event that triggered this webhook.

object

Information about the owner of the webhook endpoint.

owner_type
string
Value: "campaign"

Always "campaign" for current implementation.

subtext_uuid
string

Unique identifier for the campaign.

object

Event-specific data payload.

object
subtext_uuid
string

UUID of the subscriber.

phone_number
string

Phone number of the subscriber.

first_name
string

First name of the subscriber.

last_name
string

Last name of the subscriber.

email
string

Email address of the subscriber.

postal_code
string

Postal code of the subscriber.

city
string

City where subscriber is located.

state
string

State where subscriber is located.

metadata
object

Additional metadata for the subscriber.

signup_method
string

How the subscriber was added to the campaign.

subscribed_at
string <date-time>

When the subscriber first subscribed (ISO 8601 format).

resubscribed_at
string <date-time>

When the subscriber last resubscribed (ISO 8601 format).

unsubscribed_at
string <date-time>

When the subscriber unsubscribed (ISO 8601 format).

Responses

Request samples

Content type
application/json
{
  • "event_type": "subscriber.resubscribed",
  • "event_owner": {
    • "owner_type": "campaign",
    • "subtext_uuid": "cb33aea2-3554-4834-9dec-f03d8f79f22a"
    },
  • "event_detail": {
    • "subscriber": {
      • "subtext_uuid": "37d7e67f-7a49-4dd5-aff6-4398613ba599",
      • "phone_number": "+14155551234",
      • "first_name": "Jane",
      • "last_name": "Smith",
      • "email": "jane.smith@example.com",
      • "postal_code": "94102",
      • "city": "San Francisco",
      • "state": "CA",
      • "metadata": {
        },
      • "signup_method": "campaign_page",
      • "subscribed_at": "2023-09-15T10:30:00Z",
      • "resubscribed_at": "2023-10-15T14:32:15Z",
      • "unsubscribed_at": null
      }
    }
}

Shortlink Direct Message Clicked Webhook

Triggered when a subscriber clicks a Subtext shortlink in a direct message. Provides click tracking and analytics for your campaign links.

Authorizations:
basicAuth
Request Body schema: application/json
event_type
string
Enum: "subscriber.message.received" "subscriber.subscribed" "subscriber.unsubscribed" "subscriber.resubscribed" "shortlink.direct_message_clicked" "webhook_endpoint.edited"

The type of event that triggered this webhook.

object

Information about the owner of the webhook endpoint.

owner_type
string
Value: "campaign"

Always "campaign" for current implementation.

subtext_uuid
string

Unique identifier for the campaign.

object

Event-specific data payload.

object
subtext_uuid
string

UUID of the subscriber who clicked the link.

phone_number
string

Phone number of the subscriber.

first_name
string or null

First name of the subscriber.

last_name
string or null

Last name of the subscriber.

email
string or null

Email address of the subscriber.

object
id
integer

Internal ID of the shortlink.

last_clicked
string or null <date-time>

ISO 8601 timestamp of the last click.

short_url
string <uri>

The shortened URL.

destination
string <uri>

The destination URL that the shortlink redirects to.

clicks
integer

Total number of clicks on this shortlink.

Responses

Request samples

Content type
application/json
{
  • "event_type": "shortlink.direct_message_clicked",
  • "event_owner": {
    • "owner_type": "campaign",
    • "subtext_uuid": "cb33aea2-3554-4834-9dec-f03d8f79f22a"
    },
  • "event_detail": {
    • "subscriber": {
      • "subtext_uuid": "32d83ce1-e983-44cc-8bcb-6d1458e05617",
      • "phone_number": "+15551234567",
      • "first_name": "John",
      • "last_name": "Doe",
      • "email": "john.doe@example.com"
      },
    • "shortlink": {}
    }
}

Webhook Endpoint Edited Webhook

Triggered when a webhook endpoint configuration is modified or updated. This allows you to track changes to your webhook settings and respond accordingly.

Authorizations:
basicAuth
Request Body schema: application/json
event_type
string
Enum: "subscriber.message.received" "subscriber.subscribed" "subscriber.unsubscribed" "subscriber.resubscribed" "shortlink.direct_message_clicked" "webhook_endpoint.edited"

The type of event that triggered this webhook.

object

Information about the owner of the webhook endpoint.

owner_type
string
Value: "campaign"

Always "campaign" for current implementation.

subtext_uuid
string

Unique identifier for the campaign.

object

Event-specific data payload.

object
url
string <uri>

The URL of the webhook endpoint that was edited.

Responses

Request samples

Content type
application/json
{
  • "event_type": "webhook_endpoint.edited",
  • "event_owner": {
    • "owner_type": "campaign",
    • "subtext_uuid": "cb33aea2-3554-4834-9dec-f03d8f79f22a"
    },
  • "event_detail": {}
}