Skip to content
Akedly

Utilities from the CLI

Set up a utility pipeline from the terminal, see what still blocks its first send, and check your server's signed requests before going live.

Check what blocks the first send

bash
akedly utilities doctor
akedly utilities doctor --pipeline-id <pipeline-id>

utilities doctor only reads. It sends nothing, changes nothing and never contacts Meta. Without --pipeline-id it checks the first active utility pipeline, or the first utility pipeline when none is active. Each failed check carries the next command to run; run the doctor again after each step.

CheckPasses when
utility-pipelineA utility pipeline is found. If not, the other checks are skipped.
pipeline-activeThe pipeline is active. New pipelines start inactive.
channelsWhatsApp, SMS or both are enabled on the pipeline.
whatsappWhatsApp is off, or your WhatsApp Business Account is connected and at least one country is allowed.
smsSMS is off, or the pipeline uses --byo-sms on and one of your own SMS providers is active, tested and able to route for it.
templateAn approved template can be sent on an enabled channel. With a ready SMS channel, free text is enough.
request-signingThe request-signing secret was saved and acknowledged, or signing is off for the pipeline. With signing off the server accepts unsigned requests, so a pass does not mean signing is on.
api-keyAn active API key covers the pipeline. A key being revoked passes with a warning to replace it.
balanceBalance is available.
webhooksAlways passes; says whether a webhook URL is set. Webhooks are optional.

A failure exits 1 with DOCTOR_FAILED and the checks in details.checks. When every check passes, data.next lists the server-side steps. A read your login may not make fails only its own check. The output shows the checked pipeline id and counts, never phone numbers, emails, names, balance amounts, key prefixes, or provider and template names.

With both channels enabled, a channel that is not ready is reported but does not fail the run while the other one can send; WhatsApp counts only with an approved WhatsApp template. It does not check the daily spend cap, per-key spend limits, suppressed recipients or routing to a specific destination. With SMS ready, the template check passes on free text alone; confirm a template is approved before sending it. For login and server problems, run akedly doctor.

Set up in order

bash
akedly pipelines create --product utility --name "Order updates" --secret-file ./utility-webhook.secret
akedly pipelines update <pipeline-id> --status active --yes
akedly whatsapp connect --wait
akedly utilities pipelines channels set <pipeline-id> --file ./channels.json
akedly utilities templates library
akedly utilities templates create --file ./template.json
akedly utilities templates submit <template-id> --yes
akedly utilities pipelines signing-secret reveal <pipeline-id> --secret-file ./utility-signing.secret
akedly utilities pipelines signing-secret acknowledge <pipeline-id> --secret-file ./utility-signing.secret --yes
akedly api-keys create --label "Order updates" --scope selected --pipeline-ids <pipeline-id> --secret-file ./utility-api-key.secret
akedly utilities doctor --pipeline-id <pipeline-id>

WhatsApp sign-up finishes in a browser at Meta, and a template waits for approval before it can send. If the server refuses a selected-scope key with API_KEY_SCOPE_NOT_ACTIVE, create it with --scope all --yes instead. To send SMS through your own provider, use akedly byo-sms create, test and set-active, run akedly pipelines update <pipeline-id> --byo-sms on --yes, then enable SMS with channels set. Every flag and consent rule is in the configuration and templates reference.

Check a signature offline

Your server signs every utility request with the pipeline's request-signing secret (usk_), not its webhook secret (whsec_). Check one request before going live:

bash
akedly utilities signature verify --secret-file ./utility-signing.secret --headers ./headers.json --body ./body.json

headers.json

{
  "x-akedly-timestamp": "1890000000000",
  "x-akedly-nonce": "nonce_fixture_001",
  "x-akedly-signature": "<hex signature your server computed>"
}

The body file must hold the exact bytes your server sends. The command needs no login and makes no request, prints no body, secret or signature, and reads the secret from AKEDLY_UTILITY_SIGNING_SECRET when --secret-file is absent. The timestamp is Unix milliseconds and must be within five minutes of now; pass --at <unix-seconds> to check an older request.

A refusal uses the code Akedly's server would return: MISSING_SIGNATURE, SIGNATURE_TIMESTAMP_SKEW (with details.reason timestamp_not_milliseconds when the timestamp looks like seconds) or INVALID_SIGNATURE. Only the server can detect a reused nonce. The signing scheme and helpers are in Request signing.

Next: send a message, utility webhooks, or the operations reference.