Skip to main content

UC-006 — Bulk SMS Campaign to Thousands of Recipients

FieldValue
IDUC-006
GoalCreate and send a bulk SMS campaign with cost estimation and monitoring
ChannelSMS
ComplexityIntermediate
Estimated time20 minutes
APIs involvedPOST /api/partner-gateway/v1/campaigns, PUT /campaigns/{id}, GET /campaigns/{id}/price, PUT /campaigns/{id}/confirm, GET /campaigns/{id}

Real-world scenarios​

  • ShopItalia — Black Friday: Flash promotion to 10,000 customers with a personalized discount code and a 48-hour expiration. Immediate send on Friday morning at 08:00.
  • Studio Dentistico Bianchi — Weekly reminders: Every Monday the system sends a reminder of the week's appointments to all booked patients.
  • AcquaLuce Energia — Bill due date: Payment due notice to all customers with unpaid invoices, with a link to the payment portal.

Prerequisites​

Before you begin, make sure you have:

Test without costs

The campaign API has no simulation mode, but nothing is sent and no credit is used until you confirm the campaign (Step 4): you can create, configure and price a draft freely.

Campaign lifecycle​

The diagram illustrates the state transitions: from creation (Draft) to configuration, cost estimation, confirmation, and finally sending.

Step 1 — Create the campaign (Draft)​

Create a new campaign in draft state specifying name, description, channel (sendingMode) and how recipients are chosen (destinationType: 0, from contact lists).

curl -X POST https://api.qlara.ai/api/partner-gateway/v1/campaigns \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"name": "Black Friday 2026",
"description": "Promozione Black Friday - sconto 30% su tutto il catalogo",
"sendingMode": "SMS",
"destinationType": 0
}'

Response — Campaign created​

{
"id": 1542,
"name": "Black Friday 2026",
"description": "Promozione Black Friday - sconto 30% su tutto il catalogo",
"status": "DRAFT",
"sendingMode": "SMS",
"destinationType": 0,
"readyToSend": false,
"creationDate": "2026-04-09 09:00:00.000+0000"
}
Save the campaign ID

The id field is the identifier you will use in all subsequent steps. Save it to track the campaign.

Step 2 — Configure message and recipients (READY_TO_SEND)​

Associate the contact list, sender, and message text with the campaign. readyToSend: true marks the configuration as final, so the campaign can be confirmed.

curl -X PUT https://api.qlara.ai/api/partner-gateway/v1/campaigns/1542 \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"sendingMode": "SMS",
"smsSender": "ShopItalia",
"smsBody": "Black Friday! Solo per te: -30% su tutto con il codice BF2026. Valido fino al 30/11. Scopri di più: https://shop.example.it/bf",
"contactListIds": [87],
"readyToSend": true
}'

Response — Campaign configured​

{
"id": 1542,
"name": "Black Friday 2026",
"status": "READY_TO_SEND",
"sendingMode": "SMS",
"smsSender": "ShopItalia",
"smsBody": "Black Friday! Solo per te: -30% su tutto con il codice BF2026. Valido fino al 30/11. Scopri di più: https://shop.example.it/bf",
"destinationType": 0,
"contactListIds": [87],
"readyToSend": true,
"lastUpdateDate": "2026-04-09 09:02:00.000+0000"
}
Contact list

The IDs in contactListIds must refer to already created lists. See UC-007 — Manage Contacts and Lists to create and populate lists.

Step 3 — Check the price​

Check the total price before proceeding with confirmation.

curl -X GET https://api.qlara.ai/api/partner-gateway/v1/campaigns/1542/price \
-H "X-Api-Key: YOUR_API_KEY"

Response — Estimated price​

{
"id": 1542,
"name": "Black Friday 2026",
"status": "READY_TO_SEND",
"sendingMode": "SMS",
"contactListIds": [87],
"totalPrice": 34408500000,
"walletKind": "EURO",
"numOfDistinctContact": 9831,
"readyToSend": true
}

numOfDistinctContact is the number of distinct recipients (9,831) and totalPrice the total price of the campaign, in the unit of the wallet named by walletKind: for EURO, hundred-millionths of a euro, so 34408500000 is €344.085.

Behind the scenes — Recipients and price

The price is computed on every call from the campaign's current configuration:

  1. Deduplication: The selected lists are merged and each phone number is counted once (numOfDistinctContact).
  2. Rate: The price depends on the sendingMode, the number of recipients and your account's per-message rate.
  3. SMS parts: A text within 160 GSM-7 characters (70 UCS-2) is a single SMS; a longer one is sent as several concatenated parts, up to 1530 characters.
  4. Always current: Every call prices the campaign again, so after changing lists, text or sendingMode just call it again. A campaign without a valid recipient answers 400.
Insufficient credit

If your credit does not cover totalPrice, confirmation will fail with 400. Top up your credit before proceeding.

Step 4 — Confirm and start sending​

Confirm the campaign to start immediate sending. The request has no body.

curl -X PUT https://api.qlara.ai/api/partner-gateway/v1/campaigns/1542/confirm \
-H "X-Api-Key: YOUR_API_KEY"

Response — Campaign confirmed​

202 Accepted, with no body: the campaign is queued and, since it has no scheduledDate, sending starts right away.

Variant — Scheduled send​

To schedule sending at a future date, set scheduledDate (format yyyy-MM-dd HH:mm:ss.SSSZ) on the campaign before confirming it:

curl -X PUT https://api.qlara.ai/api/partner-gateway/v1/campaigns/1542 \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"sendingMode": "SMS",
"scheduledDate": "2026-11-29 08:00:00.000+0000",
"readyToSend": true
}'

Then confirm it with PUT /campaigns/1542/confirm as above: the campaign is sent at the scheduled date. Every later update of the campaign must carry scheduledDate and readyToSend: true again: an update without them removes the schedule and moves the campaign back to DRAFT.

Step 5 — Monitor progress​

Check the campaign's delivery counters during and after sending.

curl -X GET "https://api.qlara.ai/api/partner-gateway/v1/campaigns/1542" \
-H "X-Api-Key: YOUR_API_KEY"

Response — Campaign completed​

{
"id": 1542,
"name": "Black Friday 2026",
"status": "ENDED",
"sendingMode": "SMS",
"startDate": "2026-04-09 09:05:02.000+0000",
"endDate": "2026-04-09 09:47:00.000+0000",
"totalDestinations": 9831,
"totalPrice": 34408500000,
"walletKind": "EURO",
"totalSent": 9831,
"totalSuccess": 9542,
"totalFailed": 289,
"smsSent": 9831,
"smsPending": 0,
"smsSuccess": 9542,
"smsFailed": 289
}

Final result: 9,542 messages delivered (totalSuccess) out of 9,831 sent (totalSent), a 97.06% delivery rate. For the outcome of each single message, export the delivery reports of the campaign (see UC-029).

Common errors​

400 Bad Request — Contact list not configured​

{
"status": "fail",
"data": {
"contactListIds": "At least one contact list is required to confirm the campaign"
}
}

Solution: Make sure you have completed Step 2 (configuration) before checking the price and confirming.

400 Bad Request — Insufficient credit​

{
"status": "fail",
"data": {
"credit": "Insufficient credit to cover the campaign totalPrice"
}
}

Solution: Top up your credit from the platform dashboard before confirming the campaign.

Expected result​

StepActionResult
1POST /campaignsCampaign in DRAFT state, id returned
2PUT /campaigns/{id}State READY_TO_SEND, text and list associated
3GET /campaigns/{id}/pricetotalPrice and numOfDistinctContact
4PUT /campaigns/{id}/confirm202 Accepted, sending starts
5GET /campaigns/{id}State ENDED, delivery counters

Next steps​