Skip to main content

UC-030 — Complete Template Lifecycle (Test, Campaign, Analysis)

FieldValue
IDUC-030
GoalManage the full lifecycle of a WhatsApp template: creation, approval, test, campaign and analysis
ChannelWhatsApp
Complexity⭐⭐⭐ Advanced
Estimated time30 minutes (excluding Meta approval times)
APIs involvedPOST /api/message-server/whatsapp/templates, GET /api/message-server/whatsapp/templates/{templateId}, POST /api/message-server/whatsapp/send, POST /api/partner-gateway/v1/campaigns, PUT /api/partner-gateway/v1/campaigns/{id}/confirm, GET /api/partner-gateway/v1/campaigns/{id}, POST /api/partner-gateway/v1/exports/delivery-reports, GET /api/partner-gateway/v1/exports/{exportId}

Real-world scenarios​

  • FashionOutlet — New promotion launch: The marketing team creates a WhatsApp template for the summer sales, tests it in simulation, then uses it for a campaign to 20,000 VIP customers. After the campaign concludes, they export results to calculate ROI.
  • ClinicaSalute — New service template onboarding: The clinic creates a template to promote a new telemedicine service. After Meta approval, they test it on a small group, verify the visual rendering and then launch the campaign.
  • BancaAdriatica — Template A/B testing: The CRM team creates two template variants for a personal loan promotion. They test both on reduced samples, compare delivery and read rates, then use the winning variant for the bulk send.
Composite use case

This UC combines the flows from UC-013 — WhatsApp Template Workflow, UC-006 — Bulk Campaign and UC-011 — Export Delivery Reports into a complete lifecycle.

Prerequisites​

Before you begin, make sure you have:

Test without costs

Add "simulation": true to the send request (Step 3) to validate the template without actually sending the message. Campaigns have no simulation mode: nothing is sent until you confirm them.

Lifecycle flow​

The diagram illustrates the complete cycle: from template creation to campaign, through to results analysis for iteration and improvement.

Step 1 — Create the WhatsApp template​

Submit the template to Meta for approval with the WhatsApp API. The phoneNumberId query parameter associates it with your WhatsApp Business number, and {firstName} is a variable filled in at send time:

curl -X POST "https://api.qlara.ai/api/message-server/whatsapp/templates?phoneNumberId=5" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "promo_estate_2026",
"lang": "it",
"category": "MARKETING",
"headerFormat": "IMAGE",
"headerMediaUrl": "https://fashionoutlet.it/media/saldi-estate-2026.jpg",
"body": "Ciao {firstName}! Saldi estivi FashionOutlet: fino al 40% di sconto. Offerta valida fino al 30 giugno!",
"footer": "FashionOutlet - Moda per tutti",
"buttons": [
{ "type": "URL", "text": "Scopri le offerte", "url": "https://fashionoutlet.it/saldi?ref=campaign_estate_2026" },
{ "type": "QUICK_REPLY", "text": "Non mi interessa" }
]
}'

Response — Template submitted​

The API answers 201 Created with the template:

{
"id": 187,
"phoneNumberId": 5,
"name": "promo_estate_2026",
"slug": "promo_estate_2026",
"category": "MARKETING",
"lang": "it",
"status": "PENDING",
"createdAt": "2026-05-20 09:00:00.000+0000",
"updatedAt": "2026-05-20 09:00:00.000+0000",
"headerMedia": {
"url": "https://fs-lora-namespace-public.fsn1.your-objectstorage.com/4d8f5607/830b788d/9c106b57985ab66cf5c96ca1e9cc95b8?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Signature=...",
"mimeType": "image/jpeg",
"thumbnailUrl": "https://fs-lora-namespace-public.fsn1.your-objectstorage.com/4d8f5607/830b788d/2b7e4d1c0f9a8e6d5c4b3a2918f7e6d5?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Signature=...",
"thumbnailMimeType": "image/jpeg"
},
"headerFormat": "IMAGE",
"body": "Ciao {firstName}! Saldi estivi FashionOutlet: fino al 40% di sconto. Offerta valida fino al 30 giugno!",
"footer": "FashionOutlet - Moda per tutti",
"buttons": [
{ "type": "URL", "text": "Scopri le offerte", "url": "https://fashionoutlet.it/saldi?ref=campaign_estate_2026" },
{ "type": "QUICK_REPLY", "text": "Non mi interessa" }
],
"placeholders": ["firstName"]
}
Meta approval times

Template approval by Meta typically takes from a few hours to 24 hours. MARKETING templates take longer than UTILITY or AUTHENTICATION ones. Configure a webhook to receive the approval or rejection notification.

Step 2 — Wait for approval and check the status​

Periodically check the template status:

curl -X GET "https://api.qlara.ai/api/message-server/whatsapp/templates/187" \
-H "X-Api-Key: YOUR_API_KEY"

Response — Template approved​

The template comes back with the same fields as at creation, now in APPROVED status (abridged here):

{
"id": 187,
"phoneNumberId": 5,
"name": "promo_estate_2026",
"slug": "promo_estate_2026",
"category": "MARKETING",
"lang": "it",
"status": "APPROVED",
"createdAt": "2026-05-20 09:00:00.000+0000",
"updatedAt": "2026-05-20 11:42:10.000+0000",
"headerFormat": "IMAGE",
"body": "Ciao {firstName}! Saldi estivi FashionOutlet: fino al 40% di sconto. Offerta valida fino al 30 giugno!",
"placeholders": ["firstName"]
}
Behind the scenes — Quality Score and limits
  1. Quality Score: GREEN/YELLOW/RED based on user interactions. A low score limits volume.
  2. Sending tier: From tier 1 (1K conversations/day) to tier 4 (unlimited), grows with quality.
  3. Rejection: Common reasons -- aggressive language, missing opt-out, improper placeholders. Edit and resubmit.

Step 3 — Test the template in simulation​

Before the campaign, verify the template with a simulation send ("simulation": true):

curl -X POST https://api.qlara.ai/api/message-server/whatsapp/send \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY" \
-d '{
"destination": "+393471234567",
"phoneNumberId": 5,
"simulation": true,
"template": {
"id": 187,
"mediaUrl": "https://fashionoutlet.it/media/saldi-estate-2026.jpg"
},
"placeholders": {
"firstName": "Marco"
}
}'

Response — Simulation successful​

{
"messageId": "e5f6a7b8-c9d0-1e2f-3a4b-5c6d7e8f9a0b",
"simulation": true,
"results": { "whatsapp": { "accepted": true } }
}
Real test send

After the simulation, send a real test to your personal number (without "simulation": true) to verify the visual rendering on the device.

Step 4 — Create and launch the campaign​

With the template tested, create the campaign on the same WhatsApp number and confirm the send. The template variables are filled in from each contact of the list:

curl -X POST "https://api.qlara.ai/api/partner-gateway/v1/campaigns" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Saldi Estate 2026 - WhatsApp VIP",
"sendingMode": "WHATSAPP",
"destinationType": 0,
"contactListIds": [2208],
"whatsappPhoneNumberId": 5,
"whatsappTemplateId": 187,
"readyToSend": true
}'

Response​

{
"id": 4630,
"name": "Saldi Estate 2026 - WhatsApp VIP",
"status": "READY_TO_SEND",
"sendingMode": "WHATSAPP",
"destinationType": 0,
"contactListIds": [2208],
"whatsappPhoneNumberId": 5,
"whatsappTemplateId": 187,
"readyToSend": true
}

Check the price with GET /campaigns/{id}/price, then confirm the send with PUT /campaigns/{id}/confirm (202 Accepted).

Step 5 — Monitor and export​

Monitor with GET /campaigns/{id} until status: "ENDED", then export the report with POST /exports/delivery-reports (see UC-029 for the full export flow).

{
"id": 4630,
"name": "Saldi Estate 2026 - WhatsApp VIP",
"status": "ENDED",
"sendingMode": "WHATSAPP",
"totalDestinations": 20150,
"totalSent": 20150,
"totalSuccess": 18935,
"totalFailed": 695,
"totalRead": 12480,
"waSent": 20150,
"waPending": 520,
"waSuccess": 18935,
"waFailed": 695,
"waRead": 12480
}

18,935 delivered out of 20,150 is a 93.97% delivery rate; 12,480 read out of 18,935 delivered is a 65.91% read rate.

Expected result​

StepActionResult
1POST /whatsapp/templatesTemplate submitted, status: "PENDING"
2GET /whatsapp/templates/{id}status: "APPROVED"
3POST /whatsapp/send (simulation)simulation: true, accepted: true
4POST /campaigns + PUT /confirmCampaign created and confirmed (202 Accepted)
5GET /campaigns/{id} + exportstatus: "ENDED", 93.97% delivered, downloadable CSV

Complete end-to-end example​

#!/bin/bash
API_KEY="YOUR_API_KEY"
PG="https://api.qlara.ai/api/partner-gateway/v1"
MS="https://api.qlara.ai/api/message-server"

# 1. Create template
TPL_ID=$(curl -s -X POST "${MS}/whatsapp/templates?phoneNumberId=5" -H "X-Api-Key: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{"name":"promo_estate_2026","lang":"it","category":"MARKETING","body":"Ciao {firstName}! Saldi estivi: -40% fino al 30 giugno!"}' | jq -r '.id')

# 2. Wait for approval
while true; do
S=$(curl -s "${MS}/whatsapp/templates/${TPL_ID}" -H "X-Api-Key: ${API_KEY}" | jq -r '.status')
[[ "$S" == "APPROVED" ]] && break; [[ "$S" == "REJECTED" ]] && exit 1; sleep 300
done

# 3. Simulation test
curl -s -X POST "${MS}/whatsapp/send" -H "Content-Type: application/json" -H "X-Api-Key: ${API_KEY}" \
-d '{"destination":"+393471234567","phoneNumberId":5,"simulation":true,"template":{"id":'"${TPL_ID}"'},"placeholders":{"firstName":"Marco"}}' | jq .

# 4. Campaign → 5. Monitor → Export (see UC-029 for the full export flow)
CMP_ID=$(curl -s -X POST "${PG}/campaigns" -H "X-Api-Key: ${API_KEY}" -H "Content-Type: application/json" \
-d '{"name":"Saldi Estate 2026","sendingMode":"WHATSAPP","destinationType":0,"contactListIds":[2208],"whatsappPhoneNumberId":5,"whatsappTemplateId":'"${TPL_ID}"',"readyToSend":true}' | jq -r '.id')
curl -s -X PUT "${PG}/campaigns/${CMP_ID}/confirm" -H "X-Api-Key: ${API_KEY}" > /dev/null

Variants​

A/B Testing with two templates​

Create two variants (e.g., urgent tone vs friendly), wait for approval of both, then launch two campaigns on different segments of the same list. Compare totalSuccess and totalRead out of totalSent of the two campaigns with GET /campaigns/{id}, or in their reports.

Template with tracked opt-out button​

The Quick Reply "Non mi interessa" button generates an INBOUND webhook with the button text, which you can use to update the contact's preferences (see UC-028).

Common errors​

Template rejected by Meta​

The template comes back in REJECTED status, with the reason given by Meta in rejectedReason (abridged here):

{
"id": 187,
"name": "promo_estate_2026",
"slug": "promo_estate_2026",
"category": "MARKETING",
"lang": "it",
"status": "REJECTED",
"rejectedReason": "ABUSIVE_CONTENT",
"updatedAt": "2026-05-20 11:42:10.000+0000"
}

Solution: rejectedReason is one of ABUSIVE_CONTENT, INCORRECT_CATEGORY, INVALID_FORMAT, PROMOTIONAL, SCAM, TAG_CONTENT_MISMATCH; otherInfoTitle and otherInfoDescription, when present, add Meta's details. Common reasons: aggressive language, unrealistic promises, missing sender identity. Edit the template with PATCH /api/message-server/whatsapp/templates/{templateId}, sending its whole content again: it goes back to review.

Campaign with unapproved template​

{ "status": "fail", "data": { "error": "Template 'promo_estate_2026' is not in APPROVED status" } }

Solution: Only templates with status: "APPROVED" can be used in campaigns. Check the status before creating the campaign.

Next steps​

References​