---
title: "Show what the account can do and what it is missing"
method: GET
path: "/api/v1/capabilities"
tags: ["Capabilities"]
---

# Show what the account can do and what it is missing

`GET /api/v1/capabilities`

## Request

- Base URL: `https://wardenpoint.com` (the document declares no server; this is the origin it was published from)
- URL: `https://wardenpoint.com/api/v1/capabilities`
- Auth: API key in header `X-API-Key`

Start here before configuring anything. Answers, in one call: which channels exist, which are switched on for this installation, which the plan permits, which the company has already credentialed, and who can be reached right now.

Without it you would be guessing. Adding a Viber contact to a company that never configured Viber succeeds — and then alerts vanish silently, because nothing on the create path knows the channel cannot deliver.

How to use the answer:

1. Read top-level `blockers` first. They mean nothing is delivered at all; per-channel fixes will not help until they clear.
2. Pick channels with `status` `ready` (work now) or `needs_setup` (worth proposing). Skip `disabled` entirely — an operator switched those off and the customer cannot change it. Mention `plan_blocked` only if suggesting an upgrade. `status` is one verdict shared with the other two state surfaces: `GET /recipients/{recipient}/availability` and the `deliverable` flag of `GET /recipients/{recipient}/routes` answer the same question about the same channel, so a channel that is not `ready` here is never reported as working there. It covers the installation's own transports too — email over an unconfigured mailer reads `needs_setup` with `email_not_configured`, not `ready`, even though there is nothing for the customer to fill in.
3. For a `needs_setup` channel, act on the blocker's `remedy`. `request_credential_link` is the common one: POST to the given `endpoint`, show the returned URL to a human, and poll its `status_url` until `verified`. Never ask a human to paste a token to you — it would end up in your context, your transport logs and your conversation history, and cannot be erased from any of them.
4. Before promising delivery, check `recipients.deliverable`. Channels that work with nobody on them deliver nothing.
5. Read `onboarding_window` before deciding how many recipients or groups to create. While it is active the plan caps on those are not applied, so design what the company actually needs instead of squeezing the design into a limit that is not currently in force — and say when it ends.

Secrets are never returned by this endpoint, under any ability. You get whether credentials are set, whether the provider accepted them, and the names of the fields still missing.

Read-only and free of side effects: safe to poll while walking a human through setup. Reflects the state at the moment of the call — re-read it after any change rather than caching.

## Response `200`

The account's capabilities and everything currently standing in the way.

- object
  - `data` CapabilityOverview: Everything needed to decide what to configure next: what channels exist, which of them work here, what the company has already set up, and who can currently be reached.
    - `channels` ChannelCapability[]: Every active contact type on this installation, including the ones this company cannot use — you cannot offer what you cannot see.
      - `contact_type` string: Slug to pass as `contact_type` when creating a contact, e.g. `email`, `phone`, `telegram`, `telegram_bot_chat_id`, `slack`, `msteams`, `whatsapp`, `viber`, `internal_sip`, `external_sip`.
      - `name` string: Display name in the account language.
      - `provider` string, nullable: Delivery provider behind this contact type, e.g. `platform_asterisk` for `phone`. Informational — you never pass it when creating a contact.
      - `delivery_channels` string[]: What this contact can carry: `text_message`, `voice_message`, `voice_call`. A recipient reachable only by `text_message` will never take a call, however many contacts they have.
      - `status` 'ready' | 'needs_setup' | 'plan_blocked' | 'disabled': `ready` — a contact of this type will actually deliver. `needs_setup` — worth offering, but something is missing; see `blockers`. `plan_blocked` — the plan does not include it; only the account owner can change that. `disabled` — switched off for this whole installation. Do not offer it and do not try to create contacts of this type.
      - `usable` boolean: Shorthand for `status == "ready"`. True means a contact added now delivers now.
      - `enabled` boolean: Whether this installation exposes the channel at all. `false` means an operator switched it off — no plan change and no credential fixes it.
      - `feature_flag` string, nullable: Name of the installation flag governing the channel, or `null` when it has none. Useful only for telling a human which switch to ask about.
      - `plan_allows` boolean: Whether the company plan includes this channel. When `false`, creating a contact of this type answers 422 however well configured everything else is.
      - `contact` object: How a contact of this type gets its address.
        - `binding` 'value' | 'external' | 'provisioned': `value` — you supply the address yourself. `external` — a human presses Connect or messages the bot and the address arrives by itself; you may create the contact empty beforehand. `provisioned` — the platform allocates the address; send the literal `__create_new__` as the value and read back what was assigned.
        - `value_required` boolean: Whether a non-empty `value` must accompany the create call. `false` only for externally bound types.
        - `agent_can_complete` boolean: Whether you can take this channel all the way to working without a human at a screen. `true` only for `value` binding.
        - `example` string, nullable: A well-formed address of this type. Follow its shape rather than reverse-engineering the pattern.
        - `validation_pattern` string, nullable: The PCRE the value is checked against, exactly as stored. `null` when any string is accepted.
      - `credential` CapabilityCredential: Whether the company credentials a channel needs are in place. Values are never returned — not in full, not masked, not by length. What you get is whether they are set, whether the provider accepted them, and the NAMES of what is still missing, which is all you need to tell a human what to fetch.
        - `provider` string: Credential provider slug, e.g. `telegram_bot`, `viber`, `whatsapp`, `custom_asterisk`. Use it in the credential-link path.
        - `configured` boolean: Every required field has a value. Says nothing about whether the provider accepts them — see `verified`.
        - `verified` boolean: The credentials were checked against the provider and worked. Delivery is only safe to promise once this is `true`.
        - `missing_fields` string[]: Names of required fields with no value yet, e.g. `["token"]`. Empty when nothing is missing. When no credential row exists at all, this lists every required field.
        - `secret_fields` string[]: Which of the fields are secrets, i.e. the ones a human types into the credential-link form. Fields not listed here are ordinary settings.
        - `request_link_supported` boolean: Whether `POST /api/v1/credentials/{provider}/request` will issue a link for this provider RIGHT NOW. It is false in two cases and you do not have to tell them apart: the provider has no secret-entry form, or the operator switched secret-entry links off for this whole installation. Asking anyway answers 422 in the first case and 404 in the second. Whenever this is false the accompanying blocker names `open_dashboard` instead — a human opens `dashboard_route` and types the credentials in, which always works.
        - `request_link_endpoint` string, nullable: The exact path to POST to for a credential link, or `null` when this provider cannot issue one.
        - `dashboard_route` string: Named dashboard route where a human enters these credentials by hand.
      - `recipients_with_contact` integer: How many active recipients already hold a contact of this type. Zero on a `ready` channel means the channel works but nobody is on it.
      - `blockers` CapabilityBlocker[]: Empty when `status` is `ready`.
        - `code` string: Stable reason code, e.g. `telegram_bot_not_configured`, `phone_no_did`, `viber_plan_blocked`, `feature_disabled`, `no_usable_channel`, `no_active_recipients`, `no_deliverable_recipients`. New codes appear as channels are added — treat an unrecognised code as "cannot fix automatically" and fall back to `remedy`.
        - `message` string: Human-readable explanation in the account language. Relay it; do not parse it.
        - `remedy` 'request_credential_link' | 'open_dashboard' | 'human_binding_required' | 'add_contact' | 'create_recipient' | 'upgrade_plan' | 'contact_operator': What removes this blocker. * `request_credential_link` — call `endpoint` to get a one-time link, show it to a human, then poll the returned `status_url`. The secret never passes through you. * `open_dashboard` — a human must open `dashboard_route` and fill something in; no API path exists. * `human_binding_required` — a human must press Connect or message the bot; the contact value arrives by itself afterwards. * `add_contact` — you can fix this: `POST /api/v1/recipients/{recipient}/contacts`. * `create_recipient` — you can fix this: `POST /api/v1/recipients`. * `upgrade_plan` — the account owner must change plan; nothing in the API changes it. * `contact_operator` — the person running this WardenPoint installation must act. Not something the customer can change. Stop offering the channel.
        - `endpoint` string, nullable: API path to call next, already complete — no placeholders except `{recipient}` where a recipient UUID belongs. `null` when no API call helps.
        - `dashboard_route` string, nullable: Named dashboard route a human should open, e.g. `dashboard.integrations.channels`. `null` when there is no such page.
    - `recipients` CapabilityRecipients: Whether there is anybody an alert would actually reach. Working channels with nobody on them still deliver nothing, and that gap is invisible from the channel list alone.
      - `total` integer: Recipients in the company, active or not.
      - `active` integer: Recipients not switched off. Inactive ones are never contacted.
      - `deliverable` integer: Active recipients holding at least one active, non-empty contact on a channel whose `status` is `ready`. This is the number that decides whether an alert lands.
      - `without_deliverable_contact` object[]: Active recipients that would receive nothing right now, named so you can fix them. Capped at 25 entries.
        - `uuid` string, uuid
        - `name` string
      - `without_deliverable_contact_truncated` boolean: True when more recipients are affected than the list shows. Fixing only the listed ones will not clear the problem.
    - `blockers` CapabilityBlocker[]: Account-wide problems, as opposed to per-channel ones. Read these first: they mean nothing gets delivered at all, so fixing a single channel will not help until they are cleared. Empty when the account can deliver.
      - `code` string: Stable reason code, e.g. `telegram_bot_not_configured`, `phone_no_did`, `viber_plan_blocked`, `feature_disabled`, `no_usable_channel`, `no_active_recipients`, `no_deliverable_recipients`. New codes appear as channels are added — treat an unrecognised code as "cannot fix automatically" and fall back to `remedy`.
      - `message` string: Human-readable explanation in the account language. Relay it; do not parse it.
      - `remedy` 'request_credential_link' | 'open_dashboard' | 'human_binding_required' | 'add_contact' | 'create_recipient' | 'upgrade_plan' | 'contact_operator': What removes this blocker. * `request_credential_link` — call `endpoint` to get a one-time link, show it to a human, then poll the returned `status_url`. The secret never passes through you. * `open_dashboard` — a human must open `dashboard_route` and fill something in; no API path exists. * `human_binding_required` — a human must press Connect or message the bot; the contact value arrives by itself afterwards. * `add_contact` — you can fix this: `POST /api/v1/recipients/{recipient}/contacts`. * `create_recipient` — you can fix this: `POST /api/v1/recipients`. * `upgrade_plan` — the account owner must change plan; nothing in the API changes it. * `contact_operator` — the person running this WardenPoint installation must act. Not something the customer can change. Stop offering the channel.
      - `endpoint` string, nullable: API path to call next, already complete — no placeholders except `{recipient}` where a recipient UUID belongs. `null` when no API call helps.
      - `dashboard_route` string, nullable: Named dashboard route a human should open, e.g. `dashboard.integrations.channels`. `null` when there is no such page.
    - `onboarding_window` OnboardingWindow, nullable: The opening period of a new account, during which the plan limits on how much the company may HOLD are not applied at all — recipients, groups, seats, API keys, outbound webhooks, alert templates and the notification quotas. It exists because the free plan allows one group and five recipients, and the first thing anyone wants to build — "routine to everybody, critical to whoever is on duty" — cannot be expressed within that. A product that cannot be evaluated cannot be bought. **Configure for what the company actually needs, not for the plan.** While `active` is true, creating a sixth recipient or a second group succeeds. What is already created is never deleted or disabled when the window closes: the limits are checked before creating, so nine recipients stay nine and the tenth is refused. Tell the customer what will and will not be creatable afterwards rather than pre-shrinking the design. Limits that cost real money are NOT lifted — rented phone numbers, TTS characters, voice minutes, SMS. Neither is the non-tariff daily delivery ceiling described in `DeliveryOverageState`. `null` means either the feature is switched off on this installation or the company never had a window — in both cases the plan limits apply normally.
      - `active` boolean: True while the window is running and plan limits are not being applied.
      - `started_at` string, date-time, nullable: When the window opened — normally the moment the company was created.
      - `ends_at` string, date-time, nullable: When the window closes. Already in the past when `active` is false.
      - `days_left` integer: Whole days remaining, rounded up. 0 once the window has closed.
      - `lifted_limits` string[]: Exactly which plan limits are not being applied right now, by their limit key. Empty once the window has closed.

## Other responses

- `401`: API key missing, invalid, revoked or expired.
- `403`: API key lacks the config.read ability. `config.write` alone does not grant it — a key used to create things does not automatically get to enumerate what the company has configured.

---

[API](https://skmtc.dev/wardenpoint/apis/wardenpoint-api.md) · [All operations](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/llms.txt) · [OpenAPI document](https://skmtc.dev/wardenpoint/apis/wardenpoint-api/revisions/259ee445728e?raw)
