Skip to content

    Arabic or English · ↑↓ to move · Enter to open · Esc to close

    العربية
    On this page

    Campaigns (bulk)

    Create and start a campaign

    POST/v2/campaigns
    • Scope:campaigns:write
    • Idempotency-Key:required
    • sends messages

    Content: text, image/video/audio/document (media.url), location, contact, cta_url, buttons, list. Recipients inline (≤10 000 with variables), contactListId or audience.

    Parameters

    Headers

    NameTypeConstraintsDescription
    Idempotency-KeyrequiredTypestringConstraints≤ 191 charsDescriptionYour unique id for this operation (≤ 191 visible ASCII). A retry with the same key and body returns the first answer with Idempotent-Replayed: true — it never runs twice. Kept 24 h.
    X-Request-IdoptionalTypestringConstraints—DescriptionOptional: your own request id, echoed back (otherwise we generate req_…). Quote it to support.

    Body

    NameTypeConstraintsDescription
    numberIdrequiredTypestringConstraints—DescriptionNumber to act on (id from GET /v2/numbers). A key restricted to some numbers can use only those.
    namerequiredTypestringConstraints≤ 120 charsDescriptionCampaign name for your reports (≤ 120).
    contentrequiredTypeobjectConstraints—DescriptionWhat to send: { type, … } with text, image / video / audio / document (media.url), location, contact, cta_url, buttons or list. Use {variable|fallback} and [[spin|tax]] so texts differ.
    recipientsoptionalTypeobject[]Constraints≤ 10000 itemsDescriptionInline audience (≤ 10 000). Or use contactListId / audience instead.
    recipients[].phoneoptionalTypestringConstraints—DescriptionRecipient number, international digits. Invalid and opted-out numbers are skipped, never sent.
    recipients[].variablesoptionalTypeobjectConstraints—DescriptionValues for {placeholders} in the content, e.g. { "name": "Mona" }.
    contactListIdoptionalTypestringConstraints—DescriptionSend to a saved contact list instead of inline recipients.
    audienceoptionalTypeobjectConstraints—DescriptionSelect contacts by lists and tags.
    audience.listIdsoptionalTypearrayConstraints—DescriptionContact list ids to include.
    audience.tagsoptionalTypearrayConstraints—DescriptionContacts with any of these tags.
    audience.excludeTagsoptionalTypearrayConstraints—DescriptionSkip contacts with any of these tags.
    optInConfirmedrequiredTypebooleanConstraintsmust be trueDescriptionMust be true: you confirm every recipient agreed to hear from you. Required for every campaign.
    speedoptionalTypestringConstraintsone of: SAFE · NORMAL · FASTDescriptionPacing preset (default NORMAL). SAFE is the slowest and the best choice for young numbers; every preset stays inside the Number Shield caps.
    scheduleAtoptionalTypestring (date-time)Constraints—DescriptionStart later (ISO 8601). Quiet hours still apply.
    startoptionalTypebooleanConstraintsdefault trueDescriptionfalse creates a draft; start it later with POST /v2/campaigns/{id}/start. Drafts never started are deleted after 30 days.

    Request

    curl -X POST "https://mursilat.com/api/v2/campaigns" \
      -H "Authorization: Bearer $WASLA_API_KEY" \
      -H "Idempotency-Key: order-1042-confirm" \
      -H "Content-Type: application/json" \
      -d '{
      "numberId": "cm1abcd2345efgh6789ijkl",
      "name": "عرض الجمعة",
      "content": {
        "type": "cta_url",
        "body": {
          "text": "أهلاً {name|عميلنا} 👋 خصم 20% لحد الجمعة"
        },
        "cta": {
          "title": "شوف العرض",
          "url": "https://example.com/offer?ref={name}"
        }
      },
      "recipients": [
        {
          "phone": "201001234567",
          "variables": {
            "name": "منى"
          }
        },
        {
          "phone": "966501234567"
        }
      ],
      "optInConfirmed": true,
      "speed": "SAFE"
    }'
    Request — JSON
    {
      "numberId": "cm1abcd2345efgh6789ijkl",
      "name": "عرض الجمعة",
      "content": {
        "type": "cta_url",
        "body": {
          "text": "أهلاً {name|عميلنا} 👋 خصم 20% لحد الجمعة"
        },
        "cta": {
          "title": "شوف العرض",
          "url": "https://example.com/offer?ref={name}"
        }
      },
      "recipients": [
        {
          "phone": "201001234567",
          "variables": {
            "name": "منى"
          }
        },
        {
          "phone": "966501234567"
        }
      ],
      "optInConfirmed": true,
      "speed": "SAFE"
    }

    Response

    201Example

    201 — application/json
    {
      "id": "cm2camp0001xyz",
      "numberId": "cm1abcd2345efgh6789ijkl",
      "name": "عرض الجمعة",
      "status": "scheduled",
      "contentType": "cta_url",
      "createdVia": "api",
      "speed": "SAFE",
      "scheduledAt": null,
      "startedAt": "2026-10-13T09:00:00.000Z",
      "pausedAt": null,
      "completedAt": null,
      "pause": null,
      "progress": {
        "total": 500,
        "queued": 380,
        "sent": 120,
        "delivered": 115,
        "read": 70,
        "replied": 9,
        "failed": 0,
        "skipped": 3,
        "optOuts": 1,
        "percent": 24
      },
      "replies": null,
      "eta": {
        "remaining": 380,
        "perDayCap": 200,
        "remainingToday": 80,
        "estimatedMinutesToday": 34,
        "extraDays": 2,
        "note": "Estimate from the number warm-up caps and pacing."
      },
      "purged": false,
      "purgedAt": null,
      "createdAt": "2026-10-13T08:55:00.000Z",
      "updatedAt": "2026-10-13T09:30:00.000Z",
      "recipientsUpload": {
        "total": 2,
        "invalid": 0,
        "notOnWhatsApp": 0,
        "unchecked": 0
      }
    }

    Response schema

    Fields of the example answer. Expand objects and arrays.

    • responseobject20 fields
      • idstring "cm2camp0001xyz"
      • numberIdstring "cm1abcd2345efgh6789ijkl"
      • namestring "عرض الجمعة"
      • statusstring "scheduled"
      • contentTypestring "cta_url"
      • createdViastring "api"
      • speedstring "SAFE"
      • scheduledAtnull null
      • startedAtstring "2026-10-13T09:00:00.000Z"
      • pausedAtnull null
      • completedAtnull null
      • pausenull null
      • progressobject10 fields
        • totalinteger 500
        • queuedinteger 380
        • sentinteger 120
        • deliveredinteger 115
        • readinteger 70
        • repliedinteger 9
        • failedinteger 0
        • skippedinteger 3
        • optOutsinteger 1
        • percentinteger 24
      • repliesnull null
      • etaobject6 fields
        • remaininginteger 380
        • perDayCapinteger 200
        • remainingTodayinteger 80
        • estimatedMinutesTodayinteger 34
        • extraDaysinteger 2
        • notestring "Estimate from the number warm-up caps and pacing."
      • purgedboolean false
      • purgedAtnull null
      • createdAtstring "2026-10-13T08:55:00.000Z"
      • updatedAtstring "2026-10-13T09:30:00.000Z"
      • recipientsUploadobject4 fields
        • totalinteger 2
        • invalidinteger 0
        • notOnWhatsAppinteger 0
        • uncheckedinteger 0

    Errors

    Besides the authentication, rate-limit and validation errors every endpoint can return:

    Errors →

    Number Shield & behaviour

    Campaigns over 50 new contacts need a {variable} or [[spintax]] so texts differ.

    The Number Shield paces the campaign (speed preset within the warm-up caps), waits during quiet hours (default 22:00–09:00) and skips invalid or opted-out numbers.

    It pauses the campaign by itself on low delivery (< 80 % over the last hour), many opt-outs (> 3 per 100) or a WhatsApp rate limit — 1 hour, then 24 hours. You get campaign.status with the reason; platform pauses can’t be lifted early (CAMPAIGN_RESUME_BLOCKED).

    Try it

    Send this request from your browser to the Mursilat API.

    documented example

    Preview — this copy of the docs isn’t on mursilat.com, so the console shows the documented example instead of calling the API.

    Real requests use your real number and plan: they can send WhatsApp messages, spend quota and delete data. (sends messages)

    Kept only in this tab’s memory: never saved, never put in the URL, sent only to mursilat.com. Reloading the page forgets it.

    Send this request for real?

    This will send a real WhatsApp message (or campaign / code) from your number and use your plan quota.

    POST /v2/campaigns