Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syllable.ai/llms.txt

Use this file to discover all available pages before exploring further.

A campaign is a planned and organized effort to reach out to a list of contacts over a period of time, with specified operating hours and rate of outreach (e.g., 1 call per hour). To learn more, check out our Campaign Tutorial Video and blog post .

Creating a campaign

To create a campaign, navigate to “Campaigns” in the sidenav, and click “Create campaign” in the top right. campaigns_create.png
  • Name: Name of your campaign
  • Description (optional): Description of your campaign.
  • Labels (optional): Add labels or tags.
  • Mode: Select mode from Voice, SMS, Email.
  • Channel source: Select a channel source, which is assigned to an outbound agent, that will be doing the outreach
  • Display ID: Enter the phone number or caller ID that your recipients will see
  • Rate per hour: Set the rate of outreach per hour (e.g., If set to 60, it will try to do 1 outreach per minute)
  • Campaign hours: Set the days and hours of operation for your campaign. You can set it so that it will only run during weekdays, or normal business hours of 9 to 5.

Batches

A batch is a grouping of phone numbers to call. Phone numbers to call are populated in a batch by uploading a CSV or manually adding a phone number. A campaign can have multiple batches, but a batch can only have one file upload.

Creating a batch

To create a batch, click on a campaign, click the “Batches” tab and “Create batch” when you are viewing a campaign. campaigns_batch_create.png
  • Expires on (optional): Can set an expiration date for the campaign to end. This may or may not occur after all the contacts are reached out to.
    • Note: Phone numbers in the batch will not be called after the expiry time
  • Auto-run batch: This toggle is to immediately start running the campaign on the batch.
    • If enabled, the campaign will automatically start reaching out to the contact list when you click “Save”. And new phone numbers added to the batch will be automatically called.
    • If disabled, the campaign will be paused when you click “Save”, and you will need to click “Start” to have it begin reaching out.

Uploading a batch

You can create a batch in a few ways:
  • Directly through the API endpoint
  • Upload a CSV file in the UI
To upload a file in the UI, click “Save and Upload”. Batch CSVs must contain the following columns: (Download template)
  • reference_id: A unique identifier
  • target: Phone number in format +14155551234
  • Any other columns can be referenced in the agent prompt or message. For example, if the CSV has a column “first-name”, the agent message can be configured to say something like “Hello vars.first-name” when that phone number is called. These additional columns are also accessible by the prompt. Reference Variables for more details.
campaigns_batch_template.png campaigns_batches.png Batch statuses (See API reference):
  • Active: When batch upload is successful and campaign is running (e.g., Auto-run is enabled)
  • Paused: When batch upload is successful and campaign is paused (e.g., Auto-run is disabled)
  • Pending: When batch is created, but there is no file uploaded yet or is being processed
  • Failed: When batch upload is unsuccessful
  • Canceled: When batch is canceled
  • Expired: When batch is expired
Tip: While a campaign can go for weeks, it is best to have a batch run for a day.

Testing your campaign

To test your campaign configuration and batch file upload, you can try sending one test call to see if it follows the specified business hours and rate of outreach.
  • Go to a campaign, click on Batches tab
  • Click on a batch and add a test number to the “Add to queue” section
  • By clicking “Add”, it will be added to the batch, and will follow the campaign settings for business hours and rate of outreach.
    • Note: Depending on your campaign settings, you may need to wait until the next scheduled outreach.
campaigns_batch_status.png

Campaign statuses

When running outbound campaigns, each request (voice call, SMS message, or email) moves through multiple statuses.

Voice

StatusMeaning
DUPLICATECommunication was filtered out by validation as a duplicate
INVALIDCommunication was filtered out by validation for having an invalid destination
PENDINGCommunication has passed validation and is waiting to be sent
CANCELEDCommunication had been PENDING but batch was canceled
COMPLETEDCall was successfully placed without voicemail detection enabled
BUSYTwilio: “Twilio dialed the number, but received a busy response.”
FAILEDTwilio: “Twilio’s carriers could not connect the call. Possible causes include the destination is unreachable, or the number may have been input incorrectly.”
NO-ANSWERTwilio: “Twilio dialed the number but no one answered before the timeout parameter value elapsed. This can be configured for each call, but by default is set to 60 seconds on outbound API calls, and 30 seconds on outbound <Dial> calls.”
MACHINECall was successfully placed with voicemail detection enabled - voicemail detection determined that the call reached a voicemail
HUMANCall was successfully placed with voicemail detection enabled - voicemail detection determined that the call reached a human
UNKNOWNCall was successfully placed with voicemail detection enabled - voicemail detection was not able to determine whether the call reached a voicemail or a human

SMS

StatusMeaning
DUPLICATECommunication was filtered out by validation as a duplicate
INVALIDCommunication was filtered out by validation for having an invalid destination
PRIOR_UNSUBSCRIBEDCommunication was filtered out by validation because a previous communication to the same user has status UNSUBSCRIBED
PENDINGCommunication has passed validation and is waiting to be sent
CANCELEDCommunication had been PENDING but batch was canceled
SENTTwilio: “Twilio has received a confirmation from our Super Network partner advising they have accepted the message. Twilio has not received updated delivery information for your message. Typically, a sent status will be replaced by a delivered or undelivered status within seconds or minutes. Please note that US/Canada long code MMS often remain in sent status due to a lack of final delivery status updates from carriers. Once a message is older than 72 hours, receiving a further status is unlikely, although we will update the record if we receive one. Your messages will stay in sent status forever if you are sending messages using the deprecated /SMS/Message REST API Resource, which does not support delivery status updates.”
ACCEPTEDTwilio: “(Messaging Services only) Twilio has received your request to create the message from a Messaging Service. Twilio is determining the optimal ‘From’ number from your service.”
QUEUEDTwilio: “Twilio has received your request to create the message. All new messages sent from a specific Twilio phone number are created with a status of queued.”
SENDINGTwilio: “Twilio is in the process of sending the message. This status is usually only present for a very short time, and in most instances will not be visible in the console or logs.”
UNDELIVEREDTwilio: “Twilio has received a delivery receipt indicating that the message was not delivered. This can happen for a number of reasons including carrier content filtering, availability of the destination handset, etc.”
DELIVERY_FAILEDTwilio “failed” status: “The message could not be sent. This can happen for various reasons including queue overflows, account suspensions and media errors (in the case of MMS).”
DELIVERY_UNKNOWNTwilio: “(Displayed in Messaging Insights) This indicates that a message has remained in the sent status for longer than 1 hour without receiving a further status update. This status is only shown in Messaging Insights; SMS records will remain in the sent status in the Message Log or /Messages API.”
DELIVEREDTwilio SMS: “Twilio has received confirmation of message delivery from the carrier, (and, where available, the destination handset).”
UNSUBSCRIBEDDuring the conversation, the user opted out

Email

StatusMeaning
DUPLICATECommunication was filtered out by validation as a duplicate
INVALIDCommunication was filtered out by validation for having an invalid destination
PRIOR_UNSUBSCRIBEDCommunication was filtered out by validation because a previous communication to the same user has status UNSUBSCRIBED
PRIOR_SPAM_REPORTCommunication was filtered out by validation because a previous email to the same user has status SPAM_REPORT
PRIOR_DROPPEDCommunication was filtered out by validation because a previous email to the same user has status DROPPED
PRIOR_BOUNCEDCommunication was filtered out by validation because a previous email to the same user has status BOUNCED
PENDINGCommunication has passed validation and is waiting to be sent
CANCELEDCommunication had been PENDING but batch was canceled
PROCESSEDEmail has been accepted by SendGrid and will be sent
DROPPEDSendGrid: “There are a number of reasons your email will not even be sent to a recipient for delivery. This event informs your system when an email has been dropped. Further, it provides a reason for the drop, such as if we’ve found spam content (if spam checker app is enabled) or we see the recipient has unsubscribed previously.”
DEFERREDSendGrid: “When an email cannot immediately be delivered, but it hasn’t been completely rejected, the deferred event fires. Sometimes called a soft bounce, SendGrid will continue to try for 72 hours to deliver a deferred message. After 72 hours, the deferral turns into a block.”
BOUNCEDSendGrid: “If a server cannot or will not deliver a message, SendGrid fires a bounce event. Bounces often are caused by outdated or incorrectly entered email addresses. Many times you won’t know a bounced email address until it bounces, so this event can help you ensure it doesn’t bounce again by removing it from your lists.”
DELIVEREDSendGrid: Email has been accepted by the receiving server (we don’t know for a fact that it’s in the user’s inbox at this point, but can get more info if it moves to OPENED, CLICKED, SPAM_REPORT, or UNSUBSCRIBED)
OPENEDSendGrid: “This event fires every time your email is viewed with images turned on”
CLICKEDRecipient clicked a link in the email body
SPAM_REPORTDuring the conversation, the user reported the email they received as spam
UNSUBSCRIBEDDuring the conversation, the user unsubscribed

Voicemail detection

You can enable voicemail detection for your campaigns. When a call connects, the system can determine whether the answer is from a live person or a voicemail system. If a voicemail is detected, the agent can leave a pre-recorded message for the recipient. If no voicemail is detected, the agent or workflow proceeds with a live interaction, based on the agent’s instructions. Configuration Options You can configure detection timeouts to control when and how voicemail is identified:
  • Pre-speech timeout: Time allowed before the callee begins speaking. If the user does not speak within this timeout, the conversation proceeds normally.
  • Post-speech timeout: Time allowed after the callee finishes speaking. If the user stays silent for this duration, the system considers the speech ended and makes a decision.
  • Overall timeout: Maximum total duration for voicemail detection to run before returning a result.
These values can be adjusted per campaign or per workspace to balance speed and accuracy. Prompt When voicemail detection is enabled, the agent is by default instructed to leave a voicemail_message explaining why the agent called and asking the user to call back. You can override this behavior by adding information to your prompt, for example:
If you reach a voicemail set the voicemail_message to: “Hello, this is New Bay Health calling to assist you with scheduling your recent referral. We’re eager to assist you at your earliest convenience. Please give us a call back at 555-123-1234. Our scheduling team is available Monday through Friday from 7:00 AM to 5:00 PM to assist you. Thank you.”

Webhooks

Webhooks give your systems real-time visibility into call progress. When an outbound call session changes status - for example, when a call connects, fails, or completes - Syllable sends an HTTP request to your registered webhook URL with details about the event. This allows you to programmatically track call outcomes, trigger alerting on failures, and update internal records without polling or waiting for a campaign to finish.
Webhooks are configured at the campaign level. You can route different statuses to different endpoints - for example, send FAILED events to an alerting service and COMPLETED events to a logging system.

Configuring webhooks

Webhooks can be configured through the Console campaign configuration screen, the API, or the SDK. Each webhook requires:
ParameterTypeDescription
urlStringThe HTTPS URL to receive webhook payloads.
trigger_statusesArray of stringsThe call statuses that trigger this webhook (e.g., ["COMPLETED", "FAILED"]).
request_methodStringHTTP method for the request. Must be POST, PUT, or PATCH.
auth_valuesObject (optional)Authentication configuration. See Authentication.
  1. Navigate to your outbound campaign in the Console.
  2. In the campaign configuration, find the Webhooks section.
  3. Click Add webhook.
  4. Enter your HTTPS URL (e.g., https://your-server.com/hooks/syllable).
  5. Select the trigger statuses you want to receive (e.g., COMPLETED, FAILED).
  6. Choose your authentication method and provide the required credentials.
  7. Save the campaign.

Webhook payload

When a call transitions to a status that matches one of your configured triggers, Syllable sends an HTTP POST request with Content-Type: application/json.
{
  "session_id": 842,
  "to_number": "+14155550100",
  "from_number": "+12125550199",
  "batch_upload_filename": "outreach_batch_jan2026.csv",
  "channel_manager_status": "FAILED",
  "status_changed_at": "2026-01-15T18:32:07Z",
  "error_message": "The destination number is not a valid phone number"
}
FieldTypeDescription
session_idNumberUnique identifier for the outbound call session. Stable across retries - use this for correlation and deduplication.
to_numberStringThe destination phone number of the outbound call.
from_numberStringThe originating phone number that placed the call.
batch_upload_filenameString | nullThe filename of the batch upload that included this call. Use this to tie the event back to your original source file. null if not applicable.
channel_manager_statusStringThe call’s new status. See Campaign statuses for the full list.
status_changed_atString (ISO 8601)UTC timestamp of when the status change occurred. Reflects the actual event time, not the webhook delivery time.
error_messageString | nullDescription of the error if the call failed. null when no error occurred.
For most integrations, subscribing to COMPLETED and FAILED covers the primary use cases: logging successful calls and alerting on failures.

Webhook authentication

Webhook requests are authenticated using an HMAC signature to ensure they originate from Syllable. HMAC signature When you configure HMAC authentication, you provide a Base64-encoded shared secret (RFC 4648, decoding to 32–512 bytes of key material). Each webhook request will include two headers:
HeaderDescription
X-Syllable-TimestampUnix timestamp of when the webhook was sent.
X-Syllable-SignatureHMAC-SHA256 signature of {timestamp}.{raw_body}, computed with the shared secret.
To verify a request:
  1. Extract the X-Syllable-Timestamp and X-Syllable-Signature headers.
  2. Concatenate the timestamp, a literal ., and the raw request body: {timestamp}.{raw_body}.
  3. Compute the HMAC-SHA256 of that string using your shared secret.
  4. Compare your computed signature to X-Syllable-Signature using a constant-time comparison to prevent timing attacks.
  5. Reject requests where the timestamp is older than 5 minutes to mitigate replay attacks.
import hmac
import hashlib
import base64
import time
from flask import Flask, request, abort

app = Flask(__name__)

WEBHOOK_SECRET_B64 = "your-base64-encoded-secret"

@app.route("/hooks/syllable", methods=["POST"])
def handle_webhook():
    timestamp = request.headers.get("X-Syllable-Timestamp", "")
    signature = request.headers.get("X-Syllable-Signature", "")

    if abs(time.time() - int(timestamp)) > 300:
        abort(401, "Stale timestamp")

    secret = base64.b64decode(WEBHOOK_SECRET_B64)
    message = f"{timestamp}.".encode() + request.get_data()
    expected = hmac.new(secret, message, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(expected, signature):
        abort(401, "Invalid signature")

    event = request.get_json()
    print(f"Session {event['session_id']}: {event['channel_manager_status']}")

    return "OK", 200

if __name__ == "__main__":
    app.run(port=8000)

Delivery and retries

Your webhook endpoint must respond with a 2xx HTTP status code within 10 seconds to acknowledge receipt. The timeout covers the full HTTP response cycle. We recommend accepting the payload and processing it asynchronously if your handling logic may take longer. If your endpoint returns a non-2xx status code or does not respond within the timeout, Syllable retries delivery:
  • Up to 2 retry attempts (i.e., 3 total attempts) over approximately 15 minutes.
  • If all attempts fail, the delivery is marked as failed.
Webhook delivery order is not guaranteed. Use the status_changed_at timestamp to determine the correct sequence of events. Because retries may cause duplicate deliveries, your endpoint should treat webhooks as at-least-once delivery. Use session_id + channel_manager_status + status_changed_at together as a deduplication key. If you don’t receive events, check:
  • Your endpoint is publicly reachable over HTTPS.
  • The trigger statuses on the webhook match the call outcomes you’re testing.
  • Your endpoint returns 200 within 10 seconds.
  • Failed deliveries are visible in the Console under the campaign’s webhook status.

Monitoring your campaign

Keep track of your campaign’s progress by viewing the batch status or overall campaign status. You can also see the outbound campaign calls, summaries, and transcripts in Sessions. For more detailed steps, check out our Campaign Tutorial Video and blog post .