meta-events list
Review account-level WhatsApp/Meta configuration and quality webhooks received by Akedly.
When to use
- Use after Meta template approval/rejection, category, phone-quality, account-review or capability changes.
How it works
- The CLI reads /meta-events; results are restricted to the authenticated account and ordered newest first.
- Date bounds are strict YYYY-MM-DD; the end day includes the whole server-local day. Pagination defaults to page 1, 20 rows;
--alluses limit 100. - The event rows are account-level Meta events, not individual message deliveries.
Prerequisites
- An authenticated session allowed to read account analytics.
Expected outcome
Event rows and pagination metadata.
Next steps
- For deliveries, correlate a row’s transactionID or wamid with your internal investigation.
- Treat recipientPhone and message metadata as private account data.
Usage
akedly meta-events list
Inspect this command offline before preparing an operation:
akedly describe "meta-events list" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--event-type | enum | template_status | template_category | phone_quality | account_review | business_capability | Command-specific option; see the full command details below. Query parameter: type. |
--start-date | text | — | Command-specific option; see the full command details below. Query parameter: from. |
--end-date | text | — | Command-specific option; see the full command details below. Query parameter: to. |
--page | integer | 1 to unbounded | Command-specific option; see the full command details below. Query parameter: page. |
--limit | integer | 1 to 100 | Command-specific option; see the full command details below. Query parameter: limit. |
--all | boolean switch | — | Command-specific option; see the full command details below. |
--help | boolean switch | — | Show offline command help. |
--output | enum | json | human | Select JSON or human output. |
--fields | dot-paths | — | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: whatsapp:read.
Server request: GET /meta-events.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
List your account Meta events. Page defaults to 1 and limit to 20; --all defaults omitted limit to 100 and uses the shared Pagination rules. --start-date and --end-date take strict YYYY-MM-DD; --end-date includes the whole server-local end day. Read-only logins are supported. --start-date must not be after --end-date.
Worked examples
Examples are synthetic illustrations, never live account records. Replace resource IDs with your own authorized resources. Secret values are omitted here; redacted examples illustrate the shape and are not schema-validation fixtures.
Example meta-events list
Review account-level WhatsApp/Meta configuration and quality webhooks received by Akedly.
akedly meta-events list --start-date 2026-10-01 --end-date 2026-10-31
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"events": [
{
"receivedAt": "2026-10-20T12:00:00.000Z",
"eventType": "template_status",
"templateName": "verify_code",
"templateLanguage": "en",
"event": "approved",
"reason": null,
"previousCategory": null,
"qualityRating": null
}
],
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalCount": 1,
"limit": 20
}
}
}
Additional command forms
akedly meta-events list
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "event-type",
"schema": {
"type": "string",
"enum": [
"template_status",
"template_category",
"phone_quality",
"account_review",
"business_capability"
]
},
"required": false,
"wire": [
{
"path": "type",
"transform": "identity",
"role": "query",
"input": "event-type",
"contributors": [
"event-type"
]
}
]
},
{
"name": "start-date",
"schema": {
"type": "string",
"format": "date",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"required": false,
"wire": [
{
"path": "from",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"validation": {
"syntax": "Valid YYYY-MM-DD calendar date; paired bounds must be ordered."
}
},
{
"name": "end-date",
"schema": {
"type": "string",
"format": "date",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"required": false,
"wire": [
{
"path": "to",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
],
"validation": {
"syntax": "Valid YYYY-MM-DD calendar date; paired bounds must be ordered."
}
},
{
"name": "page",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]\\d*)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"parsedValueSchema": {
"type": "integer",
"minimum": 1
}
}
},
{
"name": "limit",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]|(?:1[0-9]|[2-8]\\d{1}|9[0-9])|100)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"maximum": 100,
"parsedValueSchema": {
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
},
{
"name": "all",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "all",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "all",
"inputRole": "local-control",
"transform": "identity",
"purpose": "all",
"contributors": [
"all"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/meta-events",
"query": {
"event-type": [
{
"path": "type",
"transform": "identity",
"role": "query",
"input": "event-type",
"contributors": [
"event-type"
]
}
],
"start-date": [
{
"path": "from",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"end-date": [
{
"path": "to",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
],
"page": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"limit": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
]
},
"body": {},
"ownerParams": []
}
}
Response contract
The report body contains stored report data; field availability varies by report kind and status. Optional template/event/category/reason/quality text fields are nullable; null is normal when a webhook event type does not populate that field.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"type": "object",
"properties": {
"receivedAt": {
"type": "string",
"format": "date-time"
},
"eventType": {
"type": "string",
"enum": [
"template_status",
"template_category",
"phone_quality",
"account_review",
"business_capability"
]
},
"templateName": {
"type": [
"string",
"null"
]
},
"templateLanguage": {
"type": [
"string",
"null"
]
},
"reason": {
"type": [
"string",
"null"
]
},
"event": {
"type": [
"string",
"null"
]
},
"category": {
"type": [
"string",
"null"
]
},
"previousCategory": {
"type": [
"string",
"null"
]
},
"qualityRating": {
"type": [
"string",
"null"
]
}
},
"required": [
"receivedAt",
"eventType"
],
"additionalProperties": true
}
},
"pagination": {
"type": "object",
"properties": {
"currentPage": {
"type": "integer"
},
"totalPages": {
"type": "integer"
},
"totalCount": {
"type": "integer"
},
"limit": {
"type": "integer"
}
},
"required": [
"currentPage",
"totalPages",
"totalCount",
"limit"
],
"additionalProperties": true
}
},
"required": [
"events",
"pagination"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic success response",
"value": {
"status": "success",
"data": {
"events": [
{
"receivedAt": "2026-10-20T12:00:00.000Z",
"eventType": "template_status",
"templateName": "verify_code",
"templateLanguage": "en",
"event": "approved",
"reason": null,
"previousCategory": null,
"qualityRating": null
}
],
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalCount": 1,
"limit": 20
}
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
meta-events deliveries
Investigate WhatsApp message delivery states for your account, including failures and fallback outcomes.
When to use
- Use to determine whether your account’s WhatsApp messages were sent, delivered, read or failed and whether fallback occurred.
How it works
- The CLI reads /meta-events/deliveries; results are restricted to the authenticated account and ordered newest first.
- Date bounds are strict YYYY-MM-DD; the end day includes the whole server-local day. Pagination defaults to page 1, 20 rows;
--alluses limit 100. - The summary counts every delivery status in the date range and deliberately ignores the
--statusand page filters; delivery rows omit OTP values and API keys but include full recipient phone numbers.
Prerequisites
- An authenticated session allowed to read account analytics.
Expected outcome
Delivery rows, status totals for the selected date range and pagination metadata.
Next steps
- For deliveries, correlate a row’s transactionID or wamid with your internal investigation.
- Treat recipientPhone and message metadata as private account data.
Usage
akedly meta-events deliveries
Inspect this command offline before preparing an operation:
akedly describe "meta-events deliveries" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--status | enum | pending | sent | delivered | read | failed | Command-specific option; see the full command details below. Query parameter: status. |
--start-date | text | — | Command-specific option; see the full command details below. Query parameter: from. |
--end-date | text | — | Command-specific option; see the full command details below. Query parameter: to. |
--page | integer | 1 to unbounded | Command-specific option; see the full command details below. Query parameter: page. |
--limit | integer | 1 to 100 | Command-specific option; see the full command details below. Query parameter: limit. |
--all | boolean switch | — | Command-specific option; see the full command details below. |
--help | boolean switch | — | Show offline command help. |
--output | enum | json | human | Select JSON or human output. |
--fields | dot-paths | — | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: whatsapp:read.
Server request: GET /meta-events/deliveries.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
List your own WhatsApp delivery records; full recipient phone numbers are private PII, but live OTPs and API keys are excluded. The summary ignores --status and paging and counts the whole --start-date/--end-date range. Page defaults to 1 and limit to 20; --all defaults omitted limit to 100 and uses the shared Pagination rules. --start-date and --end-date take strict YYYY-MM-DD; --end-date includes the whole server-local end day. Read-only logins are supported. --start-date must not be after --end-date.
Worked examples
Examples are synthetic illustrations, never live account records. Replace resource IDs with your own authorized resources. Secret values are omitted here; redacted examples illustrate the shape and are not schema-validation fixtures.
Example meta-events deliveries
Investigate WhatsApp message delivery states for your account, including failures and fallback outcomes.
akedly meta-events deliveries --start-date 2026-10-01 --end-date 2026-10-31
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"deliveries": [
{
"wamid": "wamid.synthetic.1",
"transactionID": "tx_synthetic_1",
"recipientPhone": "+201234567890",
"messageType": "template",
"templateName": "verify_code",
"sendStatus": "success",
"deliveryStatus": "delivered",
"sentAt": "2026-10-20T12:00:00.000Z",
"fallbackTriggered": false,
"deliveryTimestamp": null,
"lastStatusUpdate": null,
"fallbackAt": null,
"fallbackChannel": null,
"fallbackSkippedReason": null,
"byoIntegrationName": null
}
],
"summary": {
"pending": 0,
"sent": 0,
"delivered": 1,
"read": 0,
"failed": 0
},
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalCount": 1,
"limit": 20
}
}
}
Additional command forms
akedly meta-events deliveries
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "status",
"schema": {
"type": "string",
"enum": [
"pending",
"sent",
"delivered",
"read",
"failed"
]
},
"required": false,
"wire": [
{
"path": "status",
"transform": "identity",
"role": "query",
"input": "status",
"contributors": [
"status"
]
}
]
},
{
"name": "start-date",
"schema": {
"type": "string",
"format": "date",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"required": false,
"wire": [
{
"path": "from",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"validation": {
"syntax": "Valid YYYY-MM-DD calendar date; paired bounds must be ordered."
}
},
{
"name": "end-date",
"schema": {
"type": "string",
"format": "date",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"required": false,
"wire": [
{
"path": "to",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
],
"validation": {
"syntax": "Valid YYYY-MM-DD calendar date; paired bounds must be ordered."
}
},
{
"name": "page",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]\\d*)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"parsedValueSchema": {
"type": "integer",
"minimum": 1
}
}
},
{
"name": "limit",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]|(?:1[0-9]|[2-8]\\d{1}|9[0-9])|100)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"maximum": 100,
"parsedValueSchema": {
"type": "integer",
"minimum": 1,
"maximum": 100
}
}
},
{
"name": "all",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "all",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "all",
"inputRole": "local-control",
"transform": "identity",
"purpose": "all",
"contributors": [
"all"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/meta-events/deliveries",
"query": {
"status": [
{
"path": "status",
"transform": "identity",
"role": "query",
"input": "status",
"contributors": [
"status"
]
}
],
"start-date": [
{
"path": "from",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"end-date": [
{
"path": "to",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
],
"page": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"limit": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
]
},
"body": {},
"ownerParams": []
}
}
Response contract
recipientPhone is full PII; plaintext OTP and API keys are deliberately not selected. Nullable event/error and fallback fields are normal model states; sentAt is a date. Recipient phone numbers are PII. OTP and API key fields are excluded by the controller projection.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"deliveries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"wamid": {
"type": [
"string",
"null"
]
},
"transactionID": {
"type": [
"string",
"null"
]
},
"recipientPhone": {
"type": [
"string",
"null"
]
},
"messageType": {
"type": "string",
"enum": [
"template",
"text",
"media",
"location"
]
},
"templateName": {
"type": [
"string",
"null"
]
},
"companyName": {
"type": [
"string",
"null"
]
},
"sendStatus": {
"type": "string",
"enum": [
"success",
"failed",
"rate_limited",
"business_limit",
"unknown_error"
]
},
"sendError": {
"type": [
"object",
"null"
],
"additionalProperties": true
},
"deliveryStatus": {
"type": "string",
"enum": [
"pending",
"sent",
"delivered",
"read",
"failed"
]
},
"deliveryTimestamp": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"deliveryError": {
"type": [
"object",
"null"
],
"additionalProperties": true
},
"sentAt": {
"type": "string",
"format": "date-time"
},
"lastStatusUpdate": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"fallbackTriggered": {
"type": "boolean"
},
"fallbackAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"fallbackChannel": {
"type": [
"string",
"null"
]
},
"fallbackSkippedReason": {
"type": [
"string",
"null"
]
},
"byoIntegrationName": {
"type": [
"string",
"null"
]
}
},
"required": [
"sentAt"
],
"additionalProperties": true
}
},
"summary": {
"type": "object",
"properties": {
"pending": {
"type": "integer"
},
"sent": {
"type": "integer"
},
"delivered": {
"type": "integer"
},
"read": {
"type": "integer"
},
"failed": {
"type": "integer"
}
},
"required": [
"pending",
"sent",
"delivered",
"read",
"failed"
],
"additionalProperties": true
},
"pagination": {
"type": "object",
"properties": {
"currentPage": {
"type": "integer"
},
"totalPages": {
"type": "integer"
},
"totalCount": {
"type": "integer"
},
"limit": {
"type": "integer"
}
},
"required": [
"currentPage",
"totalPages",
"totalCount",
"limit"
],
"additionalProperties": true
}
},
"required": [
"deliveries",
"summary",
"pagination"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic success response",
"value": {
"status": "success",
"data": {
"deliveries": [
{
"wamid": "wamid.synthetic.1",
"transactionID": "tx_synthetic_1",
"recipientPhone": "+201234567890",
"messageType": "template",
"templateName": "verify_code",
"sendStatus": "success",
"deliveryStatus": "delivered",
"sentAt": "2026-10-20T12:00:00.000Z",
"fallbackTriggered": false,
"deliveryTimestamp": null,
"lastStatusUpdate": null,
"fallbackAt": null,
"fallbackChannel": null,
"fallbackSkippedReason": null,
"byoIntegrationName": null
}
],
"summary": {
"pending": 0,
"sent": 0,
"delivered": 1,
"read": 0,
"failed": 0
},
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalCount": 1,
"limit": 20
}
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
