Skip to navigation

Quickstart

Send your first SMS broadcast in under five minutes.
View as Markdown

1. Get an API key

Sign in to the Teekrr dashboard, go to API Management → API Keys, and create a key with the scopes you need.

ScopeEndpoints
send_smsPOST /sms
send_whatsappPOST /whatsapp, POST /whatsapp/upload-header-image
send_emailPOST /email
(none)POST /broadcasts/validate - any valid key

The plaintext key value is shown only once at creation. Teekrr stores only a hash of it. Lost keys cannot be recovered - revoke and reissue.

2. Send your first SMS

Every SMS broadcast needs a registered keyword (your sender-ID prefix) - pass it as keyword, or as keywordUid if you have the UUID. Your keywords are listed in the dashboard.

curl -X POST https://api.teekrr.com/sms \
-H "Authorization: Bearer $TEEKRR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateContent": "Hi from Teekrr!",
"campaignName": "My first broadcast",
"type": "quick broadcast",
"keyword": "TEEKRR",
"recipients": ["60123456789"]
}'

Expected response (202 Accepted):

{
"data": {
"broadcastUuid": "00000000-0000-0000-0000-000000000001",
"queued": 1,
"failed": 0,
"totalSmsUnits": 1,
"sqsMessageIds": ["11111111-2222-3333-4444-555555555555"]
}
}

The broadcast is accepted asynchronously - final delivery status reaches your registered webhook URL.

Passing templateContent without templateName creates a one-off template named after your campaignName. To reuse an existing template instead, pass templateName or templateUid.

3. Register a webhook

In the dashboard, go to API Management → Webhooks → New Webhook and provide:

  • A URL on your server (publicly reachable, HTTPS)
  • The events you want to receive: delivered, failed, sent, read, inbound
  • Optionally, the channels to narrow to (sms, whatsapp, email) - leave it unset to receive every channel

You’ll receive a plaintext signing secret once. Store it server-side and use it to verify the X-Teekrr-Signature header on every incoming webhook - see the Webhooks guide for verification snippets in Node, Python, and Go.

sent, read, and inbound are WhatsApp-only, and the email channel emits no webhooks at all - the sent / failed / total counts in the POST /email response are the complete result.

Status events fire only for broadcasts sent through the API. A broadcast composed in the dashboard emits nothing, which is the usual reason a freshly registered webhook stays quiet.

4. Send a WhatsApp broadcast

WhatsApp is template-only - templateName must reference a template your client owns that has already been approved. Templates are created and submitted for approval in the dashboard.

Template values go in a single named map, variables.templateParams, keyed by the {{name}} placeholders the template declares:

curl -X POST https://api.teekrr.com/whatsapp \
-H "Authorization: Bearer $TEEKRR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateName": "welcome_promo_v1",
"campaignName": "My first WhatsApp broadcast",
"type": "quick broadcast",
"recipients": ["60123456789"],
"variables": {
"templateParams": { "name": "Ali", "discount": "RM50 OFF" }
}
}'

variables accepts templateParams and nothing else. Sending header, body, button, headerImage, or bodyParams is rejected with 400, not ignored.

Media headers and dynamic URL buttons are ordinary named variables: upload the asset via POST /whatsapp/upload-header-image and pass the returned url as the value of whichever variable the template named.

5. Send to many recipients (dynamic mode)

To personalize content per recipient, use recipientVariables:

curl -X POST https://api.teekrr.com/sms \
-H "Authorization: Bearer $TEEKRR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateName": "otp_login",
"campaignName": "Login OTPs 2026-05-06",
"type": "quick broadcast",
"keyword": "TEEKRR",
"recipientVariables": [
{ "msisdn": "60123456789", "variables": { "code": "318204", "name": "Ali" } },
{ "msisdn": "60198765432", "variables": { "code": "771943", "name": "Fatimah" } }
]
}'

The template content Hi {name}, your OTP is {code} is resolved per-recipient before sending. Maximum 10,000 recipients per request, and duplicates are rejected, not silently dropped.

6. Pre-flight a large list

Before committing a big recipient list, dry-run it. POST /broadcasts/validate runs the same validator the send endpoints use - no credit reserved, no messages created, nothing enqueued:

curl -X POST https://api.teekrr.com/broadcasts/validate \
-H "Authorization: Bearer $TEEKRR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"recipients": ["60123456789", "0123456789", "not-a-number"]
}'
{
"data": {
"summary": { "total": 3, "valid": 1, "invalid": 2 },
"valid": [{ "row": 1, "value": "+60123456789" }],
"invalid": [
{ "row": 2, "line": 2, "field": "msisdn", "value": "0123456789",
"code": "DUPLICATE", "message": "Duplicate recipient (also on row 1)" },
{ "row": 3, "line": 3, "field": "msisdn", "value": "not-a-number",
"code": "INVALID_MSISDN", "message": "Must be a valid phone number (E.164 or Malaysia 01X format)" }
]
}
}

Malaysian numbers in local 01X… form are normalised to +60…, which is why row 2 collides with row 1.

What’s next