Your channel and its health

Read and rename your channel, read its health, and pace sending.

  • GET/v1/sessions/<CHANNEL_ID>

    Read this channel's state.

  • PATCH/v1/sessions/<CHANNEL_ID>

    Change the label or the registration date.

  • GET/v1/sessions/<CHANNEL_ID>/safety

    Read the readings, by key, and the overall verdict.

Your number is a real WhatsApp account

WAgate links your number the way WhatsApp Web does, so WhatsApp judges it like any other account. A number that sends like a robot can get restricted.

Warming

Warming builds a normal history on a new number before you use it for volume. It is optional, and it does not cap what you send yourself. The sandbox channel can warm too; its warming sends count toward its allowances.

Read the channel

bash
curl "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>" -H "Authorization: Bearer <CHANNEL_TOKEN>"
json
{
  "name": "<CHANNEL_ID>",
  "phone": "66XXXXXXXXX",
  "label": "Support line",
  "state": "CONNECTED",
  "plan": "paid",
  "daily_send_cap": null,
  "daily_send_cap_source": "none",
  "warmup_protection": false,
  "lid_lookup": false,
  "token_set": true,
  "jid": "[email protected]",
  "push_name": "Support",
  "qr_pending": false,
  "connected": true,
  "logged_in": true,
  "created_at": "2026-09-17T03:00:00Z",
  "updated_at": "2026-09-17T03:05:00Z",
  "last_message_at": "2026-09-17T03:05:00Z",
  "last_event_at": "2026-09-17T03:05:00Z",
  "connected_observed_at": "2026-09-17T03:05:10Z"
}

name, phone, label, daily_send_cap, warmup_protection, lid_lookup, state, token_set, qr_pending, created_at, updated_at, connected and logged_in are always present, including empty phone or label, false booleans and a null daily_send_cap. The API also supplies plan and daily_send_cap_source. Optional fields such as jid, push_name, last_error and the activity timestamps are omitted when they have no value.

  • state is NEW, PAIRING, CONNECTED, DISCONNECTED or BLOCKED.
  • plan is paid, sandbox, comp or internal. sandbox is the account's free channel, which has daily allowances (see Limits). paid has no gateway allowances. comp is a channel covered by the account's channel grant rather than a subscription. internal is a channel owned by the gateway operator's own account.
  • daily_send_cap is a number, or null when no cap applies. A positive number is a daily ceiling that resets at Bangkok midnight. 0 stops sends until the cap is changed; midnight does not lift a zero cap. An operator override can set either a positive ceiling or zero. daily_send_cap_source is operator, config, default or none.
  • warmup_protection is true when the optional age-based daily allowances are on for this channel. It is off by default.
  • lid_lookup says whether LID lookup is enabled for this channel. See Contacts.
  • connected_observed_at is when connected was observed. connected_stale: true means this read could not observe the connection, so connected is the last known value. The field is absent when the read is fresh.
  • last_error appears when the last connection attempt or state change failed. It is one of a fixed set of sentences and never quotes the underlying error.
  • registration_date appears when you have set one.

Rename the channel

bash
curl -X PATCH "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>" \
  -H "Authorization: Bearer <CHANNEL_TOKEN>" -H "Content-Type: application/json" \
  -d '{"label": "Support line", "registration_date": "2024-03-01"}'

The answer is the channel object.

  • label: up to 40 characters. An empty string resets it.
  • registration_date: YYYY-MM-DD, not in the future. An empty string resets it. It tells the health reading how old the number really is.
  • Leave a field out to keep it as it is.
  • Any other key is refused with 400 field_not_allowed.

Health

bash
curl "https://api-wagate.socialground.co/v1/sessions/<CHANNEL_ID>/safety" -H "Authorization: Bearer <CHANNEL_TOKEN>"
json
{
  "status": "current",
  "session": "<CHANNEL_ID>",
  "label": "Support line",
  "measured_at": "2026-09-17T03:05:00Z",
  "overall": {
    "key": "overall",
    "label": "Overall",
    "level": "good",
    "reason": "every indicator is good",
    "measured_at": "2026-09-17T03:05:00Z",
    "value": "good",
    "basis": "",
    "evidence_at": null,
    "live": false
  },
  "indicators": [
    {
      "key": "stood_down",
      "label": "Allowed to send",
      "level": "good",
      "value": "Yes",
      "reason": "Nothing is stopping this number from sending.",
      "basis": "wagate's own dial policy",
      "measured_at": "2026-09-17T03:05:00Z",
      "evidence_at": "2026-09-17T03:05:00Z",
      "live": true,
      "data": {
        "stood_down": false,
        "blocked": false,
        "quarantined": false,
        "refused": false,
        "refusal_kind": "",
        "cap_source": "none"
      }
    },
    {
      "key": "lifetime",
      "label": "Number age",
      "level": "good",
      "value": "",
      "reason": "Old enough to carry normal volume.",
      "basis": "paired",
      "measured_at": "2026-09-17T03:05:00Z",
      "evidence_at": "2026-03-01T03:05:00Z",
      "live": false,
      "data": {
        "days": 200,
        "source": "paired",
        "is_lower_bound": false,
        "linked_at": "2026-03-01T03:05:00Z"
      }
    },
    {
      "key": "address_book_coverage",
      "label": "Saved contacts",
      "level": "good",
      "value": "1 of 1 people saved",
      "reason": "Most of the people you message are saved in this number's contacts.",
      "basis": "this number's contact store, against the peers it has messaged",
      "measured_at": "2026-09-17T03:05:00Z",
      "evidence_at": "2026-09-17T03:05:00Z",
      "live": true,
      "data": {
        "saved": 1,
        "peers": 1,
        "unresolved_lid": 0,
        "faults": 0,
        "ratio": 1
      }
    },
    {
      "key": "response_rate",
      "label": "Replies",
      "level": "too_early",
      "value": "0 of 0 people replied in the last 7 days (0 messages sent)",
      "reason": "Not enough messages yet to judge. This needs messages to at least 5 people.",
      "basis": "wagate's message log, both directions",
      "measured_at": "2026-09-17T03:05:00Z",
      "evidence_at": "2026-09-17T03:05:00Z",
      "live": false,
      "data": {
        "peers_answered": 0,
        "peers_messaged": 0,
        "sends": 0,
        "window_days": 7,
        "min_people": 5,
        "min_messages": 5
      }
    }
  ]
}

API shape: report {status, measured_at, overall, indicators[]}; indicator {key, label, level, value, reason, basis, measured_at, evidence_at, live, data}.

The readings, by key:

`key`Label
stood_downAllowed to send
lifetimeNumber age
address_book_coverageSaved contacts
response_rateReplies
proxyProxy (operator and owning account only)

Channel-token view returns 4 rows (no proxy); operator and owning-account view returns 5.

Levels are good, watch, bad and too_early. overall.level is always exactly good, watch or bad, never none. It is bad if any reading is bad, else watch if any reading is watch or could not be measured or the report is waiting or stale, else good. It is the worst of the readings, never an average, and it carries the losing reading's reason. too_early is neutral: good plus too_early reads good when at least one reading is measured good; all too_early reads watch with "Not enough data yet"; a reading that could not be measured reads watch. A reading that could not be measured is watch with data.unreadable: true and value "Could not read", not good. An unrecognized level is treated as watch.

status is current when the channel is connected and the report is fresh. It is waiting when the number is not linked yet, with no retained measurements and overall.level: "watch" (reason "Not linked yet"). It is stale when a paired channel is disconnected or its connection cannot be observed fresh. A stale report keeps the last known measurements when available, and overall.level is watch (reason "Not connected right now. Showing the last known readings."), or bad when a measured reading, stood_down included, is bad. Both waiting and stale reports include a current stood_down indicator. Check status before treating the overall verdict as a current measurement.

Each row shows its source time. The footer shows "Refreshed HH:MM" from the report time. Each row has a collapsed "Show evidence" expander with raw counts, window and source time. Proxy = real test time; age = link date; live state = "Right now". Replayed rows in a stale/waiting report show "As of <report measured_at> (not connected)". Allowed to send is recomputed on every read and still shows "Right now".

Number age shows one count: calendar days elapsed from linked_at in the viewer zone, "Linked 1 Oct, 19 days ago"; lower-bound sources render "At least N days". The level uses server days.

The lifetime reading is good or watch, never bad: a young number is something to watch, not something wrong.

Only a reading about the number's state right now can be bad: stood down, or a failing proxy. Age, contacts, replies and a missing or shared proxy are things to watch.

For lifetime, a computed age has an empty value. Use data.source, data.linked_at, data.days and data.is_lower_bound to render it. A registration_date source is a calendar date encoded as UTC midnight; preserve that date rather than converting it to a local instant. Pairing and message timestamps are instants and use the viewer's zone. Count calendar days to measured_at in the viewer's zone; lower-bound ages use data.days.

For stood_down, data.refused is the policy-only refusal used by the warmer, independent of the Health level. Cap refusals and recent restriction history can change the Health level without changing this field. The stood_down, blocked, quarantined and optional since fields keep their previous meanings. The warmer still treats an allowed verdict beside one of those conditions as unreadable.

Changelog

Sending pace is no longer a health reading. A refusal from the daily cap now shows under Allowed to send. Readings are matched by key; new level too_early means not enough data yet and does not affect the overall result.

Pacing

Paid channels have no daily send cap by default; an operator override can impose a positive ceiling or a zero stop. Pacing is up to you. Space sends out with some randomness rather than firing bursts.

Warm-up protection

Optional warm-up protection is a switch in the console. With it on, group adds, group and community creation, contact checks and contact saves have their own daily allowances by number age, and a call over the allowance gets 429. With it off, a paid channel has none of these allowances. The warmup_protection field on the channel shows the switch. See Limits for the 429 codes.

When WhatsApp pushes back

Health reports what the gateway can measure. It adds no restrictions. You can unlink on your phone and link again by code or QR, through the console or API. Automatic reconnect only resumes a linked device.

Changed 2026-10-20: overall.level no longer returns none; waiting and stale reports read watch (or bad when a measured reading is bad).

Your channel and its health · WAgate docs