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}, POST /campaigns/{id}/calculateGoal, GET /campaigns/{id}/price, PUT /campaigns/{id}/confirm, GET /campaigns/stats

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:

:::tip Test without costs Add "simulation": true in the request body to validate the flow without actually sending messages and without consuming credit. :::

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, channel, and description.

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

Response — Campaign created

{
"id": 1542,
"name": "Black Friday 2026",
"channel": "SMS",
"description": "Promozione Black Friday - sconto 30% su tutto il catalogo",
"status": "DRAFT",
"createdAt": "2026-04-09T09:00:00Z"
}

:::tip 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 (Configured)

Associate the contact list, sender, and message text with the campaign.

curl -X PUT https://lora-api.agiletelecom.com/api/partner-gateway/v1/campaigns/1542 \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"sender": "ShopItalia",
"text": "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",
"contactListId": 87,
"unicode": false
}'

Response — Campaign configured

{
"id": 1542,
"name": "Black Friday 2026",
"channel": "SMS",
"status": "CONFIGURED",
"sender": "ShopItalia",
"text": "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",
"contactListId": 87,
"unicode": false,
"updatedAt": "2026-04-09T09:02:00Z"
}

:::info Contact list The contactListId must refer to an already created list. See UC-007 — Manage Contacts and Lists to create and populate lists. :::

Step 3 — Calculate cost (CostCalculated)

Before confirming, verify how many valid recipients there are and how many SMS parts will be needed.

curl -X POST https://lora-api.agiletelecom.com/api/partner-gateway/v1/campaigns/1542/calculateGoal \
-H "X-Api-Key: YOUR_API_KEY"

Response — Cost calculated

{
"id": 1542,
"status": "COST_CALCULATED",
"totalRecipients": 10247,
"validRecipients": 9831,
"invalidRecipients": 416,
"estimatedParts": 9831,
"calculatedAt": "2026-04-09T09:03:00Z"
}
Behind the scenes — Recipient validation

During calculation, the system performs these operations:

  1. Deduplication: Removes duplicate numbers present in the list.
  2. Format validation: Discards numbers not in international format or with incorrect syntax.
  3. Part estimation: Analyzes the text to determine the number of SMS per message (1 part = 160 char GSM-7, 70 char UCS-2).
  4. Blacklist: Excludes numbers present in opt-out or system blacklists.

The 416 invalid recipients in this example include duplicate, malformed, or blacklisted numbers.

Step 4 — Check the price

Check the total cost in credits before proceeding with confirmation.

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

Response — Estimated price

{
"id": 1542,
"totalRecipients": 9831,
"pricePerMessage": 0.035,
"totalPrice": 344.09,
"currency": "EUR",
"availableCredit": 1500.00,
"sufficient": true
}

:::warning Insufficient credit If sufficient is false, confirmation will fail. Top up your credit before proceeding. :::

Step 5 — Confirm and start sending

Confirm the campaign to start immediate sending.

curl -X PUT https://lora-api.agiletelecom.com/api/partner-gateway/v1/campaigns/1542/confirm \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"sendImmediately": true
}'

Response — Campaign confirmed

{
"id": 1542,
"status": "CONFIRMED",
"sendImmediately": true,
"confirmedAt": "2026-04-09T09:05:00Z"
}

Variant — Scheduled send

To schedule sending at a future date, use scheduledDate instead of sendImmediately:

curl -X PUT https://lora-api.agiletelecom.com/api/partner-gateway/v1/campaigns/1542/confirm \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"scheduledDate": "2026-11-29T08:00:00.000+0000"
}'

Step 6 — Monitor progress

Check delivery statistics in real time during and after sending.

curl -X GET "https://lora-api.agiletelecom.com/api/partner-gateway/v1/campaigns/stats?campaignId=1542" \
-H "X-Api-Key: YOUR_API_KEY"

Response — Campaign completed

{
"campaignId": 1542,
"name": "Black Friday 2026",
"status": "COMPLETED",
"stats": {
"total": 9831,
"sent": 9831,
"delivered": 9542,
"undelivered": 289,
"pending": 0,
"deliveryRate": 97.06
},
"cost": {
"totalCredits": 344.09,
"currency": "EUR"
},
"completedAt": "2026-04-09T09:47:00Z"
}

Final result: 9,542 messages delivered out of 9,831 valid recipients (97.06%), total cost 344.09 EUR.

Common errors

400 Bad Request — Contact list not configured

{
"status": "fail",
"data": {
"contactListId": "Contact list is required before calculating cost"
}
}

Solution: Make sure you have completed Step 2 (configuration) before requesting cost calculation.

402 — Insufficient credit

{
"status": "fail",
"data": {
"credit": "Insufficient credit. Required: 344.09, available: 120.00"
}
}

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 CONFIGURED, text and list associated
3POST /campaigns/{id}/calculateGoalState COST_CALCULATED, recipients validated
4GET /campaigns/{id}/priceTotal cost and sufficient credit check
5PUT /campaigns/{id}/confirmState CONFIRMED -> SENDING
6GET /campaigns/statsReal-time delivery statistics

Next steps