Pipelines, bans and webhook verification
Akedly CLI command reference: Pipelines, bans and webhook verification.
pipelines list
Compare the OTP and utility pipelines available to your account before choosing a pipeline for a flow.
When to use
- Use this when setting up an integration or checking which API versions and statuses are already available.
How it works
- Reads the account-owned pipeline collection; it does not change pipeline settings.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
A list of your pipelines with their server-returned configuration.
Next steps
- Use the intended pipeline ID with the matching OTP command; V1, V1.2, and V2 have different integration flows.
Usage
akedly pipelines list
Inspect this command offline before preparing an operation:
akedly describe "pipelines list" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--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: pipelines:read.
Server request: GET /pipelines/user/:userID.
Owner parameters resolved from the CLI Login: userID.
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 pipelines for your account.
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.
Synthetic pipelines list workflow
Compare the OTP and utility pipelines available to your account before choosing a pipeline for a flow.
akedly pipelines list
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": [
{
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"auths24h": 0,
"successRate7d": 100,
"lastActivityAt": null,
"lastSuccessAt": null,
"hasSucceededEver": false,
"version": "V1.2"
}
]
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines 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
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/user/:userID",
"query": {},
"body": {},
"ownerParams": [
"userID"
]
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"securityConfig": {
"type": "object"
},
"active": {
"type": "boolean"
},
"type": {
"type": "string"
}
},
"required": [
"_id"
],
"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 successful response",
"value": {
"status": "success",
"data": [
{
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"auths24h": 0,
"successRate7d": 100,
"lastActivityAt": null,
"lastSuccessAt": null,
"hasSucceededEver": false,
"version": "V1.2"
}
]
}
}
]
{
"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
}
}
pipelines get
Inspect one pipeline’s current API version, callbacks, verification methods, billing and security settings before changing or integrating it.
When to use
- Use before updating settings or diagnosing a pipeline-specific rejection.
How it works
- Reads the pipeline by its Mongo ID and returns the server resource.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
The current pipeline resource.
Next steps
- Use the current settings as the basis for an update; the CLI reads revisions automatically when settings require conflict protection.
Usage
akedly pipelines get <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines get" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--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: pipelines:read.
Server request: GET /pipelines/id/:id.
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
Read a pipeline, including its callback URLs.
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.
Synthetic pipelines get workflow
Inspect one pipeline’s current API version, callbacks, verification methods, billing and security settings before changing or integrating it.
akedly pipelines get 0123456789abcdef01234567
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"securityConfig": {
"apiVersion": "v1.2"
},
"hasAttachedWidget": false,
"clusterGateHomeCountries": [],
"channelChoice": {
"enabled": false
},
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines get <id>
akedly pipelines get <id> --fields data.frontendCallbackURL,data.backendCallbackURL
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/id/:id",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"securityConfig": {
"type": "object"
},
"verificationMethods": {
"type": "array"
},
"updatedAt": {
"type": "string",
"format": "date-time"
},
"active": {
"type": "boolean"
},
"type": {
"type": "string"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"securityConfig": {
"apiVersion": "v1.2"
},
"hasAttachedWidget": false,
"clusterGateHomeCountries": [],
"channelChoice": {
"enabled": false
},
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
}
]
{
"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
}
}
pipelines create
Create an OTP or utility verification pipeline so an application can use Akedly’s corresponding verification flow.
When to use
- Use when an account needs a new integration endpoint; choose V1, V1.2 or V2 deliberately for OTP, while utility pipelines use their supported utility contract.
How it works
- Builds the create request and defaults OTP pipelines to V1.2 when
--api-versionis omitted; utility pipelines use utility settings. The backend creates the pipeline inactive, and secret delivery writes to a private file only when--secret-fileis given. Without a file, webhookSigningSecret remains in the JSON response.
Prerequisites
- A CLI login with permission for this account and resource.
- For secret-safe machine output, choose a new private
--secret-filepath; omitting it places webhookSigningSecret in the JSON response.
Expected outcome
A pipeline resource with active:false by default and a generated webhook signing secret. With --secret-file, JSON gets secretFile and omits the secret; without it, webhookSigningSecret is in JSON.
Next steps
- Review the pipeline API version and callbacks, configure verification methods as needed, and activate deliberately with pipelines update <id>
--statusactive--yesbefore sending.
Usage
akedly pipelines create
Inspect this command offline before preparing an operation:
akedly describe "pipelines create" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--name | text | — | yes | Command-specific option; see the full command details below. Manifest body field: name. |
--description | text | — | no | Command-specific option; see the full command details below. Manifest body field: description. |
--api-version | enum | v1 | v1.2 | v2 | no | Command-specific option; see the full command details below. Manifest body field: securityConfig.apiVersion. Tip: Use V2 if you will embed Akedly's hosted widget, and V1.2 if you will build the verification screens yourself or cannot embed an iframe or open a system browser. V1.0 is legacy and sunsets on December 30, 2026. |
--redirect-url | text | — | no | Command-specific option; see the full command details below. Manifest body field: frontendCallbackURL. Tip: For a web page, use an https:// redirect URL; http:// is sent unencrypted, so keep it for a local test server. An app deep link such as yourapp://callback keeps its own scheme, which your app must handle. |
--webhook-url | text | — | no | Command-specific option; see the full command details below. Manifest body field: backendCallbackURL. Tip: Use an https:// webhook URL; http:// is sent unencrypted, so keep it for a local test server. Test the endpoint before it receives production traffic. |
--secret-file | text | — | no | Write the secret to a new mode 0600 file and omit it from output; an existing path is refused. |
--dry-run | boolean switch | — | no | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--product | enum | otp | utility | no | Command-specific option; see the full command details below. Manifest body field: product. |
--byo-sms | enum | inherit | on | off | no | Command-specific option; see the full command details below. Manifest body field: byoSms. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: pipelines:create.
Server request: POST /pipelines.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
Output
Sensitive output: secret-sink. Use the supported sink; default output may contain a secret. Supported sinks: --secret-file.
Full command details
Create an OTP or utility pipeline with --product otp|utility (default otp). OTP API version defaults to v1.2; V2 requires both callback URLs. Utility creation omits OTP defaults. --api-version is only for OTP pipelines; omit it with --product utility. --byo-sms inherit|on|off uses the account setting, enables BYO-SMS or disables it for this pipeline; enabling it does not prove routing eligibility. New pipelines start inactive; run akedly pipelines update <id> --status active --yes before sending. Save its webhook secret with --secret-file. For --product utility, --webhook-url receives signed utility event webhooks (delivery status, inbound messages, button actions), using this pipeline's webhook secret; --redirect-url is not used for utility messages. Utility creation enables WhatsApp for an admin account or an active WABA; otherwise it enables SMS when BYO-SMS is usable, or leaves both off. Adjust with akedly utilities pipelines channels set <id> --file <path>.
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.
Synthetic pipelines create workflow
Create an OTP or utility verification pipeline so an application can use Akedly’s corresponding verification flow.
akedly pipelines create --name 'Example OTP' --secret-file ./private/webhook-secret.txt
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"securityConfig": {
"apiVersion": "v1.2"
},
"secretFile": "./private/webhook-secret.txt",
"active": false,
"type": "Test"
},
"message": "Pipeline created successfully"
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Secret output sink: ./private/webhook-secret.txt. Use a new private path when running the command; the synthetic example is documentation only.
Additional command forms
akedly pipelines create --name Example --secret-file <new path>
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": "name",
"schema": {
"type": "string"
},
"required": true,
"wire": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
]
},
{
"name": "description",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "description",
"transform": "identity",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
]
},
{
"name": "api-version",
"schema": {
"type": "string",
"enum": [
"v1",
"v1.2",
"v2"
]
},
"required": false,
"wire": [
{
"path": "securityConfig.apiVersion",
"transform": "identity",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
}
]
},
{
"name": "redirect-url",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "frontendCallbackURL",
"transform": "identity",
"role": "body",
"input": "redirect-url",
"contributors": [
"redirect-url"
]
}
]
},
{
"name": "webhook-url",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "backendCallbackURL",
"transform": "identity",
"role": "body",
"input": "webhook-url",
"contributors": [
"webhook-url"
]
}
]
},
{
"name": "secret-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "output-sink",
"contract": "secret-output",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"fileContract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"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": "secret-file",
"inputRole": "output-sink",
"transform": "identity",
"fileContract": "secret-output",
"contributors": [
"secret-file"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
},
{
"name": "product",
"schema": {
"type": "string",
"enum": [
"otp",
"utility"
]
},
"required": false,
"wire": [
{
"path": "product",
"transform": "identity",
"role": "body",
"input": "product",
"contributors": [
"product"
]
}
]
},
{
"name": "byo-sms",
"schema": {
"type": "string",
"enum": [
"inherit",
"on",
"off"
]
},
"required": false,
"wire": [
{
"path": "byoSms",
"transform": "identity",
"role": "body",
"input": "byo-sms",
"contributors": [
"byo-sms"
]
}
]
}
],
"files": [
{
"name": "secret-output",
"contract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"uses": [
{
"flag": "secret-file",
"role": "output-sink",
"purpose": null
}
]
}
],
"wire": {
"method": "POST",
"route": "/pipelines",
"query": {},
"body": {
"name": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
],
"description": [
{
"path": "description",
"transform": "identity",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
],
"redirect-url": [
{
"path": "frontendCallbackURL",
"transform": "identity",
"role": "body",
"input": "redirect-url",
"contributors": [
"redirect-url"
]
}
],
"webhook-url": [
{
"path": "backendCallbackURL",
"transform": "identity",
"role": "body",
"input": "webhook-url",
"contributors": [
"webhook-url"
]
}
],
"api-version": [
{
"path": "securityConfig.apiVersion",
"transform": "identity",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
}
],
"product": [
{
"path": "product",
"transform": "identity",
"role": "body",
"input": "product",
"contributors": [
"product"
]
}
],
"byo-sms": [
{
"path": "byoSms",
"transform": "identity",
"role": "body",
"input": "byo-sms",
"contributors": [
"byo-sms"
]
}
]
},
"ownerParams": []
}
}
Response contract
The returned resource uses active:boolean and type (there is no status field). Newly created pipelines default to active:false.
Without --secret-file, webhookSigningSecret remains in JSON; with the file sink, the secret field is removed and secretFile is added.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"securityConfig": {
"type": "object"
},
"secretFile": {
"type": "string"
},
"webhookSigningSecret": {
"type": "string",
"pattern": "^whsec_[A-Za-z0-9+/]{43}=$"
},
"active": {
"type": "boolean"
},
"type": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--secret-file receives one-time secret delivery",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"secretFile": {
"type": "string"
}
},
"required": [
"secretFile"
],
"not": {
"required": [
"webhookSigningSecret"
]
},
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"With --secret-file, secretDelivery removes data.webhookSigningSecret and adds data.secretFile. The secret bytes are written to the selected file."
],
"title": "--secret-file receives one-time secret delivery"
},
{
"when": "--secret-file is omitted",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"webhookSigningSecret": {
"type": "string",
"pattern": "^whsec_[A-Za-z0-9+/]{43}=$"
},
"secretFile": {
"type": "string"
}
},
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"The CLI passes through data.webhookSigningSecret in JSON output. Treat stdout as secret material; do not log or expose it."
],
"title": "--secret-file is omitted"
},
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1/pipelines",
"body": {
"name": "Example OTP",
"securityConfig": {
"apiVersion": "v1.2"
}
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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": "Projection is not redaction; selecting sensitive paths can print Secrets or live OTP values. Use the command secret sink where supported and keep output private."
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"securityConfig": {
"apiVersion": "v1.2"
},
"secretFile": "./private/webhook-secret.txt",
"active": false,
"type": "Test"
},
"message": "Pipeline created successfully"
}
}
]
{
"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
}
}
pipelines update
Change a pipeline’s customer-facing verification, routing, country, branding, or security behavior while preserving concurrent edits.
When to use
- Use to adjust an existing pipeline after checking its API version and attached widgets.
How it works
- For revision-protected settings, reads the current pipeline revision and includes it in the write to reject stale edits. On V1 and V2, widget-only controls fan out to every active and inactive attached widget after a paginated attachment read; on V1.2 they are refused. Dry-run performs planning reads and returns either a raw single-request preview or an ordered multi-resource steps plan.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Updated pipeline resource, or a dry-run plan; an ordered fan-out can fail with PARTIAL_UPDATE after some resources were saved.
Next steps
- Inspect every resource named by PARTIAL_UPDATE before retrying; use pipelines get and widgets list-filtered including inactive widgets.
Usage
akedly pipelines update <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines update" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--name | text | — | Command-specific option; see the full command details below. Manifest body field: name. |
--description | text | — | Command-specific option; see the full command details below. Manifest body field: description. |
--status | enum | active | inactive | Command-specific option; see the full command details below. Manifest body field: active. |
--environment | enum | live | test | Command-specific option; see the full command details below. Manifest body field: type. |
--otp-language | enum | en | ar | Command-specific option; see the full command details below. Manifest body field: locale. |
--redirect-url | text | — | Command-specific option; see the full command details below. Manifest body field: frontendCallbackURL. Tip: For a web page, use an https:// redirect URL; http:// is sent unencrypted, so keep it for a local test server. An app deep link such as yourapp://callback keeps its own scheme, which your app must handle. |
--webhook-url | text | — | Command-specific option; see the full command details below. Manifest body field: backendCallbackURL. Tip: Use an https:// webhook URL; http:// is sent unencrypted, so keep it for a local test server. Test the endpoint before it receives production traffic. |
--smart-routing | enum | on | off | Command-specific option; see the full command details below. Manifest body field: smartRouting. |
--passkeys | enum | on | off | Command-specific option; see the full command details below. Manifest body field: passkeyEnabled. Tip: Passkeys let returning users verify with Face ID or a fingerprint, and a passkey sign-in can cost up to 40% less than an OTP at the default passkey rate; your account's rate may differ. |
--zero-verify-bans | enum | on | off | Command-specific option; see the full command details below. Manifest body field: zeroVerifyBansEnabled. Tip: Keep zero-verify bans on, as they are by default; when they are off, account-level zero-verify bans no longer stop requests to this pipeline. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--byo-sms | enum | inherit | on | off | Command-specific option; see the full command details below. Manifest body field: byoSms. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--cluster-gate | enum | on | off | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.enabled, clusterGateIntent. |
--cluster-gate-position | enum | cautious | balanced | assertive | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.position, clusterGateIntent. Tip: Balanced is the default position. Estimated during an attack, Cautious blocks about 65% of spam and refuses no real users, Balanced about 90% of spam and about 1% of real users, and Assertive 95% or more of spam and about 3% of real users. Assertive is outside Akedly's recommended limits: it does not check whether a request is a real user caught in the same flood. Refusals are rare outside an attack. |
--cluster-gate-escalation | enum | on | off | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.escalation, clusterGateIntent. |
--cluster-gate-home-countries | list | — | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.homeCountries, clusterGateIntent. The literal none clears this value. Tip: Declare the countries where you expect phone destinations and end-user IPs to help Cluster Gate understand your traffic. Home countries do not restrict sending or allow or block IP addresses, and other protections still apply. Learned countries stay included automatically; clearing declarations does not clear the learned set. |
--verification-methods | list | — | Command-specific option; see the full command details below. Manifest body field: verificationMethods. Tip: Akedly recommends WhatsApp first, for the most reliable delivery and a lower price than SMS. With automatic order, WhatsApp is already the default first channel, even when your list leaves it out, and your list decides the fallbacks; to keep WhatsApp out or to send in your own order, pass --method-order custom and list the methods in that order, because a list alone keeps the pipeline's current order setting. |
--method-order | enum | custom | automatic | Command-specific option; see the full command details below. Manifest body field: useMethodOrder. Tip: Akedly recommends WhatsApp first, for the most reliable delivery and a lower price than SMS. With automatic order, WhatsApp is already the default first channel, even when your list leaves it out, and your list decides the fallbacks; to keep WhatsApp out or to send in your own order, pass --method-order custom and list the methods in that order, because a list alone keeps the pipeline's current order setting. |
--channel-choice | enum | on | off | Command-specific option; see the full command details below. Manifest body field: channelChoiceEnabled. |
--channel-choice-mode | enum | ask-first | send-first | Command-specific option; see the full command details below. Manifest body field: channelChoiceMode. |
--channel-choice-enforcement | enum | fallback | hard-pin | Command-specific option; see the full command details below. Manifest body field: channelChoiceEnforcement. |
--rate-limits-file | text | — | Command-specific option; see the full command details below. Manifest body field: rateLimiting.perEndUserIP.perDay, rateLimiting.perEndUserIP.perHour, rateLimiting.perEndUserIP.perMinute, rateLimiting.perPhoneNumber.perDay, rateLimiting.perPhoneNumber.perHour, rateLimiting.perPhoneNumber.perMinute, rateLimiting.perPipeline.perDay, rateLimiting.perPipeline.perHour, rateLimiting.perPipeline.perMinute, expectedUpdatedAt. Tip: Start with the default limits unless your traffic needs different ones. Use traffic recommendations when enough history is available; their calculations include a 10% growth buffer. Final widget recommendations also apply per-phone and per-device caps and may raise per-widget hour and day limits to meet the required ratios, so applied values can differ from the buffered calculation. Review the result against expected peaks before changing live limits. |
--circuit-breaker | enum | on | off | Command-specific option; see the full command details below. Manifest body field: circuitBreaker.enabled. Tip: Keep the circuit breaker on; Akedly recommends it to protect verification from flood attacks and traffic spikes. |
--circuit-breaker-sensitivity | enum | strict | balanced | lenient | Command-specific option; see the full command details below. Manifest body field: circuitBreakerMultiplier. |
--destination-countries-mode | enum | all | only-selected | all-except-selected | Command-specific option; see the full command details below. Manifest body field: countryPolicy.mode, expectedUpdatedAt. |
--destination-countries | list | — | Command-specific option; see the full command details below. Manifest body field: countryPolicy.countries, expectedUpdatedAt. |
--recommended-limits | boolean switch | — | Command-specific option; see the full command details below. Manifest body field: rateLimiting.perPipeline.perMinute, rateLimiting.perPipeline.perHour, rateLimiting.perPipeline.perDay, expectedUpdatedAt. Tip: Start with the default limits unless your traffic needs different ones. Use traffic recommendations when enough history is available; their calculations include a 10% growth buffer. Final widget recommendations also apply per-phone and per-device caps and may raise per-widget hour and day limits to meet the required ratios, so applied values can differ from the buffered calculation. Review the result against expected peaks before changing live limits. |
--period | enum | last-month | last-quarter | last-6-months | Command-specific option; see the full command details below. |
--otp-template | text | — | Command-specific option; see the full command details below. Manifest body field: defaultOtpTemplateId. |
--api-version | enum | v1 | v1.2 | v2 | Command-specific option; see the full command details below. Manifest body field: securityConfig.apiVersion, expectedUpdatedAt. Tip: Use V2 if you will embed Akedly's hosted widget, and V1.2 if you will build the verification screens yourself or cannot embed an iframe or open a system browser. V1.0 is legacy and sunsets on December 30, 2026. |
--pow-starting-difficulty | text | — | Command-specific option; see the full command details below. Manifest body field: securityConfig.pow.difficulty, expectedUpdatedAt. |
--passkey-company-name | text | — | Command-specific option; see the full command details below. Manifest body field: passkeyBranding.customCompanyName, expectedUpdatedAt. |
--passkey-logo-url | text | — | Command-specific option; see the full command details below. Manifest body field: passkeyBranding.customLogo, expectedUpdatedAt. |
--passkey-primary-color | text | — | Command-specific option; see the full command details below. Manifest body field: passkeyBranding.customPrimaryColor, expectedUpdatedAt. |
--passkey-secondary-color | text | — | Command-specific option; see the full command details below. Manifest body field: passkeyBranding.customSecondaryColor, expectedUpdatedAt. |
--passkey-theme | enum | light | dark | system | Command-specific option; see the full command details below. Manifest body field: passkeyBranding.theme, expectedUpdatedAt. |
--turnstile | enum | on | off | Command-specific option; see the full command details below. Manifest body field: securityConfig.turnstile.enabled, expectedUpdatedAt. |
--turnstile-allowed-domains | list | — | Command-specific option; see the full command details below. Manifest body field: securityConfig.turnstile.domains, expectedUpdatedAt. |
--turnstile-platform | enum | web | mobile | Command-specific option; see the full command details below. Manifest body field: securityConfig.turnstile.platform, expectedUpdatedAt. |
--turnstile-site-key | text | — | Command-specific option; see the full command details below. Manifest body field: securityConfig.turnstile.siteKey, expectedUpdatedAt. |
--turnstile-test-token | text | — | Command-specific option; see the full command details below. Manifest body field: securityConfig.turnstile.testToken, expectedUpdatedAt. |
--turnstile-secret-key-file | text | — | Command-specific option; see the full command details below. Manifest body field: securityConfig.turnstile.secretKey, expectedUpdatedAt. |
--circuit-breaker-file | text | — | Command-specific option; see the full command details below. Manifest body field: circuitBreaker.floodThresholds.per15Minutes, circuitBreaker.floodThresholds.per5Minutes, circuitBreaker.floodThresholds.perMinute, circuitBreaker.suspensionDurations.firstViolation, circuitBreaker.suspensionDurations.maxViolation, circuitBreaker.suspensionDurations.secondViolation, expectedUpdatedAt. Tip: Keep the circuit breaker on; Akedly recommends it to protect verification from flood attacks and traffic spikes. |
--pow | enum | on | off | Command-specific option; see the full command details below. Manifest body field: security.pow.allowed, expectedUpdatedAt. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-trigger-percent | integer | 25 to 95 | Command-specific option; see the full command details below. Manifest body field: security.pow.triggerPercent, expectedUpdatedAt. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-maximum-difficulty | integer | 3 to 6 | Command-specific option; see the full command details below. Manifest body field: security.pow.maxDifficulty, expectedUpdatedAt. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--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: pipelines:update.
Server request: PUT /pipelines/id/:id.
Consent
--yes when --status or --environment or --otp-language or --smart-routing or --passkeys or --zero-verify-bans or --byo-sms or --cluster-gate or --cluster-gate-position or --cluster-gate-escalation or --cluster-gate-home-countries or --verification-methods or --method-order or --channel-choice or --channel-choice-mode or --channel-choice-enforcement or --rate-limits-file or --circuit-breaker or --circuit-breaker-sensitivity or --destination-countries-mode or --destination-countries or --recommended-limits or --otp-template or --api-version or --pow-starting-difficulty or --passkey-company-name or --passkey-logo-url or --passkey-primary-color or --passkey-secondary-color or --passkey-theme or --turnstile or --turnstile-allowed-domains or --turnstile-platform or --turnstile-site-key or --turnstile-test-token or --turnstile-secret-key-file or --circuit-breaker-file or --pow or --pow-trigger-percent or --pow-maximum-difficulty or --redirect-url or --webhook-url is given. --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
Retry exception: a pipelines update that changes rate limits or destination countries is not retried automatically after a timeout or server error, because it may already be saved; check with akedly pipelines get <id>, and running the same command again is safe. Update only the flags you supply: --name, --description, --status active|inactive, --environment live|test, --otp-language en|ar, --redirect-url, --webhook-url, --smart-routing on|off, --passkeys on|off, --zero-verify-bans on|off, --byo-sms inherit|on|off, --cluster-gate on|off, --cluster-gate-position cautious|balanced|assertive, --cluster-gate-escalation on|off, --cluster-gate-home-countries EG,SA (or none), --verification-methods, --method-order custom|automatic, --channel-choice on|off, --channel-choice-enforcement fallback|hard-pin and --channel-choice-mode ask-first|send-first. Results show status as the active field (true or false) and environment as the type field (Live or Test). Use none to clear a callback URL. Cluster Gate is Akedly's AI protection that refuses code requests that look like part of an attack: --cluster-gate turns it on or off for this pipeline, --cluster-gate-position chooses how much it refuses during an attack, and --cluster-gate-escalation on|off controls a stricter window that lasts 15 minutes after Cluster Gate last detects an attack; while that window is open, Cluster Gate can refuse every request to this pipeline, or only requests to unusual destinations when the attack targets numbers outside your usual countries. --cluster-gate-home-countries EG,SA replaces the complete declared home-country list for this pipeline, so include every country you want to keep. Omit the flag to preserve the saved declarations. Use the exact lowercase value none on its own to clear them; do not combine none with country codes. Use comma-separated two-letter country codes. Codes are normalized to uppercase, duplicates are removed, and the server saves a sorted list; an unknown code is refused rather than dropped. These declarations guide Cluster Gate for expected phone destinations and end-user IP countries. They do not restrict sending or allow or block IP addresses; destination-country restrictions and other protections still apply. Learned countries are managed separately by the engine; replacing or clearing declarations does not edit or erase learned countries or their history. This flag needs --yes whenever supplied, including none or the current list. A home-country-only --dry-run previews the declaration without --yes or requests; the server checks country membership and Cluster Gate availability on the real update. Read saved declarations with akedly pipelines get <id> in data.securityConfig.clusterGate.homeCountries; data.clusterGateHomeCountries contains separate learned-country metadata. This flag also works on widgets create and update, changing the linked pipeline. The CLI sends only the Cluster Gate settings you supply, plus the intent marker the server requires, so the others keep their values. If Cluster Gate cannot be changed on this pipeline, the command fails with CLUSTER_GATE_NOT_AVAILABLE, details.reason says why, and nothing in that request is saved; --dry-run cannot check this. --verification-methods takes a comma list of whatsapp, telegram, sms and email, each at most once; it replaces the whole list, so include every method you want to keep, it cannot be empty, and email always goes last. The order you type is used whenever the pipeline uses custom order, because you pass --method-order custom or the pipeline already has it. The CLI reads Akedly's public list of verification methods, which needs no login, to find each one; even a dry run makes this one read, though it changes nothing. A word with no available method is refused as VERIFICATION_METHOD_UNAVAILABLE before anything is saved. On legacy pricing, SMS cannot be the only phone method; add whatsapp or telegram. --method-order custom sends through your methods in the listed order. With automatic order, WhatsApp is the default first channel even when your list leaves it out, sent from Akedly's shared number unless you connected your own; two things can start a send elsewhere: a channel the user picked through Channel Choice, and Smart Routing, which sends someone's next code on the next channel when an earlier WhatsApp code to that number has not been confirmed as delivered. Your list then decides the fallbacks, Telegram and then SMS, only those you listed, whatever order you typed them in. To keep WhatsApp out or to send in the order you typed, use --method-order custom; email is unaffected. --method-order is sent only when you pass it, so a list alone does not change the order setting and the pipeline keeps the one it has, except that a list with exactly one phone method (email not counted) is always saved with custom order, even with --method-order automatic; pass --method-order whenever the order matters. akedly pipelines get <id> shows the current methods as ids in data.verificationMethods, the order setting in data.useMethodOrder (true means custom), data.hasAttachedWidget and the Channel Choice summary in data.channelChoice, including blockers, eligibleChannels and fallbackOrder. --channel-choice on|off lets users choose where to get their code. If their channel fails later, --channel-choice-enforcement fallback tries another channel and hard-pin uses only their choice; with custom order, fallback tries the next listed channel, and with automatic order it follows WhatsApp, then Telegram, then SMS among the methods in your list, so an unlisted WhatsApp is never a Channel Choice fallback. --channel-choice-mode sets the widget experience: ask-first lets users choose before a code is sent, and send-first sends a code, then offers a choice on resend; it applies only to V2 pipelines with a widget attached, so changing it on any other pipeline fails with SETTING_NOT_FOR_THIS_PIPELINE, while setting the mode the pipeline already has (ask-first when none is stored) skips that check, though another Channel Choice flag that changes in the same command can still be refused. Channel Choice needs it enabled on your account, granular pricing and pay-per-message billing, and it is also blocked on a utility pipeline, for an excluded account or while Akedly has it switched off for everyone. A command that changes a Channel Choice value is then refused with CHANNEL_CHOICE_NOT_AVAILABLE and details.blockers names the reason, while a value the pipeline already has, such as off when it is already off or was never set, is accepted. --rate-limits-file <file> reads a JSON object of the limits to change, for example {"perPipeline":{"perMinute":30}}: perPhoneNumber, perIpAddress and perPipeline each take perMinute, perHour and perDay as whole numbers above zero, only the limits you name change, and every other limit is kept. --circuit-breaker on|off turns the circuit breaker on or off, and --circuit-breaker-sensitivity strict|balanced|lenient sets how much traffic above your rate limits it tolerates before it suspends the pipeline; strict tolerates the least. On V1.2 these change the pipeline. On V1 or V2 the CLI applies rate limits, the circuit breaker and recommended limits to every attached widget, including inactive widgets; use the widget rate-limit file shape, with widgetAttempts and otpRequests containing perPhoneNumber, perDevice and perWidget. --circuit-breaker-file, --pow, --pow-trigger-percent and --pow-maximum-difficulty are widget settings and are refused on V1.2. --pow-starting-difficulty accepts adaptive or 2..7 on V1.2, and 3..6 on V1/V2 widgets. Sensitivity, Turnstile and pipeline passkey branding stay V1.2-only. If a V1/V2 pipeline has no widgets the command refuses with SETTING_NOT_FOR_THIS_PIPELINE and reason no-widget. The CLI checks the resulting API version when --api-version is supplied, reads both active and inactive listings through every page, checks permissions and reads every resource revision before writing. The pipeline saves first, then widgets in creation-time/id order. If a later write fails, PARTIAL_UPDATE lists confirmed resources and unconfirmed destinations; inspect them and run the same update again. --dry-run shows every planned guarded write without saving. --recommended-limits applies Akedly's recommended pipeline-wide limits from your real traffic over --period last-month|last-quarter|last-6-months (default last-month), with a 10% buffer for growth; it never changes the per-phone-number or per-IP-address limits or the circuit breaker sensitivity, cannot be combined with --rate-limits-file, and is refused with NOT_ENOUGH_TRAFFIC when the analysis has low confidence or fewer than 100 requests, so a quiet pipeline keeps its limits. --destination-countries-mode all|only-selected|all-except-selected with --destination-countries EG,SA chooses which countries can receive codes, on every pipeline version. --destination-countries replaces the saved list, so include every country you want to keep; --destination-countries-mode only-selected or all-except-selected on its own keeps the saved list, so switching between them inverts it; after the list was cleared with mode all it is empty, so switching to only-selected also needs --destination-countries. To clear the list, pass --destination-countries-mode all on its own; a list sent with mode all that matches the saved list is simply cleared, and a changed list with mode all is refused with DESTINATION_COUNTRIES_MODE_REQUIRED instead of being dropped. For rate-limit and destination-country changes the CLI first reads the pipeline and sends the revision it read, so if someone changes the pipeline in between, the command fails with PIPELINE_CHANGED instead of undoing their change; run it again to read the pipeline again. --dry-run makes the same reads and sends no change; the pipeline read needs permission to read pipelines, and the recommendation read for --recommended-limits needs permission to update them. Cooldowns cannot be set because they have no effect. For V1.2 pipelines, --pow-starting-difficulty adaptive|2..7 chooses the starting difficulty; Akedly can clamp it to the current policy ceiling. It does not change whether PoW is enabled. --passkey-company-name, --passkey-logo-url (HTTPS), --passkey-primary-color and --passkey-secondary-color (#RRGGBB), and --passkey-theme light|dark|system override the passkey look; none clears a name, logo or colour override. --api-version changes a V1.2 pipeline to V2 only; supply both callbacks before upgrading. --turnstile on|off and --turnstile-platform web|mobile manage protection. A new Web integration needs --turnstile-site-key, --turnstile-secret-key-file <path|-> and --turnstile-test-token; - reads a piped secret from stdin, never pass the secret as an argument. Obtain a fresh setup token on the docs Turnstile token page; it is single-use, so obtain another after a refused or uncertain save. Mobile uses the managed shared integration. --turnstile-allowed-domains edits an enabled existing legacy Web integration only. Security, version and branding flags require --yes, read the current revision first, never repeat an uncertain write automatically, and mask credentials in --dry-run. Changing --otp-language provisions the matching WhatsApp template on your connected WABA. --byo-sms inherit|on|off uses the account setting, enables BYO-SMS or disables it for this pipeline; enabling it does not prove routing eligibility. Product is chosen at creation and cannot be changed here. Changing status, environment, OTP language, smart routing, passkeys, zero-verify bans, BYO-SMS, any Cluster Gate setting, the verification methods, the method order, any Channel Choice setting, the rate limits, the circuit breaker, the destination countries, the recommended limits or a callback URL (--redirect-url or --webhook-url, including none) can stop, reroute or loosen live verification, so it needs --yes, also when it turns a protection off or loosens a limit; names, descriptions and --period do not, and --dry-run previews any change without --yes. Use --otp-template <id> to attach an OTP template approved for the pipeline language, or --otp-template default to restore the built-in template. A custom template needs an active WhatsApp method and your own connected WhatsApp Business Account. Both attachment and restoring the default require --yes. Find template ids and per-language approval status with akedly whatsapp otp-templates list.
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.
Synthetic pipelines update workflow
Change a pipeline’s customer-facing verification, routing, country, branding, or security behavior while preserving concurrent edits.
akedly pipelines update 0123456789abcdef01234567 --name 'Example OTP' --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Synthetic V1.2 pipeline limits and Turnstile setup
Set a per-pipeline request ceiling and enable the Turnstile web check for a V1.2 pipeline during initial protection setup.
akedly pipelines update 0123456789abcdef01234567 --rate-limits-file ./fixtures/pipeline-limits.json --turnstile on --turnstile-platform web --turnstile-site-key 0x4AAAAAAAsyntheticSiteKey --turnstile-secret-key-file ./fixtures/turnstile-secret.txt --turnstile-test-token synthetic-test-token --yes
Input file: ./fixtures/pipeline-limits.json (json).
{
"perPipeline": {
"perMinute": 30
}
}
Input file: ./fixtures/turnstile-secret.txt (text).
[REDACTED — use your privately saved secret file]
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Prerequisites: an authorized CLI login and an existing V1.2 pipeline; confirm its API version and current behavior with pipelines get before applying settings. The test token and all values are synthetic documentation values and are not usable credentials.
This is a confirmed write. The CLI reads the pipeline settings and revision before saving; dry-run still performs planning reads and previews the request. Turnstile setup-token validation can refuse the write, so use a fresh valid setup token when running for real.
The perPipeline limit controls requests across this pipeline. After success, inspect pipelines get for saved limits and Turnstile state, then finish the provider-side hostname setup if needed.
Synthetic V2 attached-widget rate and circuit-breaker limits
Adjust request ceilings and flood protection on every widget attached to an existing V2 pipeline.
akedly pipelines update 89abcdef0123456789abcdef --rate-limits-file ./fixtures/widget-rate-limits.json --circuit-breaker-file ./fixtures/widget-circuit-breaker.json --yes
Input file: ./fixtures/widget-rate-limits.json (json).
{
"otpRequests": {
"perWidget": {
"perMinute": 30
}
}
}
Input file: ./fixtures/widget-circuit-breaker.json (json).
{
"floodThresholds": {
"perMinute": 20,
"per5Minutes": 60,
"per15Minutes": 120
},
"suspensionSeconds": {
"firstViolation": 60,
"secondViolation": 300,
"maxViolation": 1800
}
}
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "89abcdef0123456789abcdef",
"name": "Example OTP",
"active": false,
"type": "Test",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Prerequisites: an authorized CLI login, an existing V2 pipeline, and its attached widgets. Check the API version and attachment list first; widget settings are refused for V1.2 pipelines.
The files use widget rate-limit and circuit-breaker input shapes. The CLI reads and revision-checks resources, then applies the supplied settings across attached widgets. If a later widget write fails, the command can return PARTIAL_UPDATE; inspect every applied and notApplied resource before retrying.
The rate ceiling is for OTP requests per individual widget. Circuit-breaker durations are supplied in seconds. After success, inspect the pipeline and attached widgets to confirm each resource's state.
Additional command forms
akedly pipelines update <id> --status active --yes
akedly pipelines update <id> --status inactive --yes
akedly pipelines update <id> --redirect-url https://example.invalid/done --webhook-url https://example.invalid/webhook --yes
akedly pipelines update <id> --webhook-url none --yes
akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes
akedly pipelines update <id> --verification-methods whatsapp,sms,email --method-order custom --yes
akedly pipelines update <id> --circuit-breaker on --circuit-breaker-sensitivity balanced --yes
akedly pipelines update <id> --cluster-gate-home-countries EG,SA --yes
akedly pipelines update <id> --cluster-gate-home-countries none --yes
akedly pipelines update <id> --cluster-gate-home-countries EG,SA --dry-run
akedly pipelines update <id> --otp-template <templateId> --yes
akedly pipelines update <id> --otp-template default --yes
akedly pipelines update <id> --pow-starting-difficulty adaptive --yes
akedly pipelines update <id> --passkey-theme dark --yes
akedly pipelines update <id> --turnstile on --turnstile-platform web --turnstile-site-key <site-key> --turnstile-secret-key-file <private-file> --turnstile-test-token <fresh-token> --yes
akedly pipelines update <id> --circuit-breaker on --pow-starting-difficulty 4 --yes
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "name",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
]
},
{
"name": "description",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "description",
"transform": "identity",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
]
},
{
"name": "status",
"schema": {
"type": "string",
"enum": [
"active",
"inactive"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "active",
"transform": "identity",
"role": "body",
"input": "status",
"contributors": [
"status"
]
}
],
"valueTranslations": {
"active": true,
"inactive": false
}
},
{
"name": "environment",
"schema": {
"type": "string",
"enum": [
"live",
"test"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "type",
"transform": "identity",
"role": "body",
"input": "environment",
"contributors": [
"environment"
]
}
],
"valueTranslations": {
"live": "Live",
"test": "Test"
}
},
{
"name": "otp-language",
"schema": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "locale",
"transform": "identity",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
]
},
{
"name": "redirect-url",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "frontendCallbackURL",
"transform": "none-empty",
"role": "body",
"input": "redirect-url",
"contributors": [
"redirect-url"
]
}
]
},
{
"name": "webhook-url",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "backendCallbackURL",
"transform": "none-empty",
"role": "body",
"input": "webhook-url",
"contributors": [
"webhook-url"
]
}
]
},
{
"name": "smart-routing",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "smartRouting",
"transform": "identity",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "passkeys",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyEnabled",
"transform": "identity",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "zero-verify-bans",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "zeroVerifyBansEnabled",
"transform": "identity",
"role": "body",
"input": "zero-verify-bans",
"contributors": [
"zero-verify-bans"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
},
{
"name": "byo-sms",
"schema": {
"type": "string",
"enum": [
"inherit",
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "byoSms",
"transform": "identity",
"role": "body",
"input": "byo-sms",
"contributors": [
"byo-sms"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "confirmation",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "confirmation",
"contributors": [
"yes"
]
}
]
},
{
"name": "cluster-gate",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.enabled",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate-position",
"schema": {
"type": "string",
"enum": [
"cautious",
"balanced",
"assertive"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.position",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
]
},
{
"name": "cluster-gate-escalation",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.escalation",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate-home-countries",
"schema": {
"type": "string",
"pattern": "^(?:[^,]*[^,\\s][^,]*)(?:,(?:[^,]*[^,\\s][^,]*))*$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"validation": {
"syntax": "Comma-separated values; every segment must contain at least one non-whitespace character."
}
},
{
"name": "verification-methods",
"schema": {
"type": "string",
"pattern": "^(?:[^,]*[^,\\s][^,]*)(?:,(?:[^,]*[^,\\s][^,]*))*$"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "verificationMethods",
"transform": "verification-method-ids"
}
],
"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": "body",
"path": "verificationMethods",
"transform": "verification-method-ids",
"input": "verification-methods",
"contributors": [
"verification-methods"
]
}
],
"validation": {
"syntax": "Comma-separated values; every segment must contain at least one non-whitespace character."
}
},
{
"name": "method-order",
"schema": {
"type": "string",
"enum": [
"custom",
"automatic"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "useMethodOrder",
"transform": "identity",
"role": "body",
"input": "method-order",
"contributors": [
"method-order"
]
}
],
"valueTranslations": {
"custom": true,
"automatic": false
}
},
{
"name": "channel-choice",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "channel-choice-mode",
"schema": {
"type": "string",
"enum": [
"ask-first",
"send-first"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceMode",
"transform": "identity",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"valueTranslations": {
"ask-first": "ask_first",
"send-first": "send_first"
}
},
{
"name": "channel-choice-enforcement",
"schema": {
"type": "string",
"enum": [
"fallback",
"hard-pin"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
],
"valueTranslations": {
"fallback": "fallback",
"hard-pin": "hard_pin"
}
},
{
"name": "rate-limits-file",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "input-file",
"contract": "pipeline-rate-limits",
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"requestTargets": [
{
"role": "body",
"path": "$",
"transform": "json-fields",
"fileContract": "pipeline-rate-limits"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision"
}
],
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Pipeline rate-limit patch",
"description": "Sparse patch in CLI file-key form. Only listed limits change. Numbers are positive safe integers; unknown keys, dotted keys, empty nested objects and an empty root object are refused. perIpAddress is mapped by the CLI to the API's perEndUserIP.",
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/limits"
},
"perIpAddress": {
"$ref": "#/$defs/limits"
},
"perPipeline": {
"$ref": "#/$defs/limits"
}
},
"minProperties": 1,
"additionalProperties": false,
"$defs": {
"limits": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perIpAddress": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPipeline": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"perPhoneNumber.perDay": {
"path": "rateLimiting.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"perPhoneNumber.perHour": {
"path": "rateLimiting.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"perPhoneNumber.perMinute": {
"path": "rateLimiting.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"perIpAddress.perDay": {
"path": "rateLimiting.perEndUserIP.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"perIpAddress.perHour": {
"path": "rateLimiting.perEndUserIP.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"perIpAddress.perMinute": {
"path": "rateLimiting.perEndUserIP.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"perPipeline.perDay": {
"path": "rateLimiting.perPipeline.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"perPipeline.perHour": {
"path": "rateLimiting.perPipeline.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"perPipeline.perMinute": {
"path": "rateLimiting.perPipeline.perMinute",
"transform": "identity",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "pipeline"
},
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Pipeline rate-limit patch",
"description": "Sparse patch in CLI file-key form. Only listed limits change. Numbers are positive safe integers; unknown keys, dotted keys, empty nested objects and an empty root object are refused. perIpAddress is mapped by the CLI to the API's perEndUserIP.",
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/limits"
},
"perIpAddress": {
"$ref": "#/$defs/limits"
},
"perPipeline": {
"$ref": "#/$defs/limits"
}
},
"minProperties": 1,
"additionalProperties": false,
"$defs": {
"limits": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
}
},
"schemaAppliesWhen": "The base schemas apply to V1.2 pipelines. Use the attached-widget variant for V1 or V2 pipelines.",
"variants": [
{
"command": "widgets update",
"when": [
"pipeline apiVersion is v1 or v2; v1.2 uses the pipeline file variant"
],
"fileContract": {
"name": "widget-rate-limits",
"definition": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"otpRequests.perDevice.perDay": {
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perHour": {
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perMinute": {
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perDay": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perHour": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perMinute": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perDay": {
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perHour": {
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perMinute": {
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perDay": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perHour": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perMinute": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perDay": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perHour": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perMinute": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perDay": {
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perHour": {
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perMinute": {
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"otpRequests": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"widgetAttempts": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"semanticSchema": {
"$ref": "#/$defs/widgetRateLimits",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"windows": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"additionalProperties": false
},
"dimensions": {
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/windows"
},
"perDevice": {
"$ref": "#/$defs/windows"
},
"perWidget": {
"$ref": "#/$defs/windows"
}
},
"additionalProperties": false
},
"widgetRateLimits": {
"type": "object",
"description": "Each perPhoneNumber/perDevice limit is capped by the server at 5 per minute, 25 per hour and 50 per day. If perWidget perMinute is supplied, perHour must be at least 5 times it and perDay at least 100 times it when those windows are supplied; the server validates the merged settings for updates.",
"properties": {
"widgetAttempts": {
"$ref": "#/$defs/dimensions"
},
"otpRequests": {
"$ref": "#/$defs/dimensions"
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"wire": {
"method": "PUT",
"route": "/widgets/:id",
"query": {},
"body": {
"name": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
],
"description": [
{
"path": "description",
"transform": "none-empty",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
],
"pipeline-id": [
{
"path": "pipelineId",
"transform": "none-null",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"status": [
{
"path": "status",
"transform": "identity",
"role": "body",
"input": "status",
"contributors": [
"status"
]
}
],
"logo": [
{
"path": "branding.logo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
},
{
"path": "branding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
}
],
"company-name": [
{
"path": "branding.companyName",
"transform": "none-empty",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
},
{
"path": "branding.customCompanyName",
"transform": "company-name-mirror",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
}
],
"favicon-url": [
{
"path": "branding.faviconUrl",
"transform": "none-empty",
"role": "body",
"input": "favicon-url",
"contributors": [
"favicon-url"
]
}
],
"widget-language": [
{
"path": "branding.appearance.language",
"transform": "identity",
"role": "body",
"input": "widget-language",
"contributors": [
"widget-language"
]
}
],
"primary-color": [
{
"path": "branding.appearance.theme.colors.primary",
"transform": "none-empty",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
},
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
}
],
"secondary-color": [
{
"path": "branding.appearance.theme.colors.secondary",
"transform": "none-empty",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
},
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
}
],
"dev-mode": [
{
"path": "devMode.enabled",
"transform": "identity",
"role": "body",
"input": "dev-mode",
"contributors": [
"dev-mode"
]
}
],
"dev-mode-digits": [
{
"path": "devMode.digits",
"transform": "identity",
"role": "body",
"input": "dev-mode-digits",
"contributors": [
"dev-mode-digits"
]
}
],
"circuit-breaker": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
}
],
"pow": [
{
"path": "security.pow.allowed",
"transform": "identity",
"role": "body",
"input": "pow",
"contributors": [
"pow"
]
}
],
"pow-trigger-percent": [
{
"path": "security.pow.triggerPercent",
"transform": "identity",
"role": "body",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
}
],
"pow-starting-difficulty": [
{
"path": "security.pow.startDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
],
"pow-maximum-difficulty": [
{
"path": "security.pow.maxDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
}
],
"smart-routing": [
{
"path": "smartRouting",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"passkeys": [
{
"path": "passkeyEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"cluster-gate": [
{
"path": "securityConfig.clusterGate.enabled",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-position": [
{
"path": "securityConfig.clusterGate.position",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-escalation": [
{
"path": "securityConfig.clusterGate.escalation",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-home-countries": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"otp-language": [
{
"path": "locale",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
],
"channel-choice": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"channel-choice-mode": [
{
"path": "channelChoiceMode",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"channel-choice-enforcement": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
]
},
"ownerParams": [],
"flag": [
{
"role": "local",
"input": "rate-limits-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-rate-limits",
"contributors": [
"rate-limits-file"
]
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"fileContract": "widget-rate-limits"
}
]
}
}
],
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "rate-limits-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"contributors": [
"rate-limits-file"
]
},
{
"role": "body",
"path": "rateLimiting.perEndUserIP.perDay",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perIpAddress.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perEndUserIP.perHour",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perIpAddress.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perEndUserIP.perMinute",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perIpAddress.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perPhoneNumber.perDay",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perPhoneNumber.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perPhoneNumber.perHour",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perPhoneNumber.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perPhoneNumber.perMinute",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perPhoneNumber.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perPipeline.perDay",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perPipeline.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perPipeline.perHour",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perPipeline.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.perPipeline.perMinute",
"transform": "identity",
"fileContract": "pipeline-rate-limits",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"valueType": "positive-integer",
"fileKey": "perPipeline.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"input": "rate-limits-file",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"fileContract": "pipeline-rate-limits"
}
]
},
{
"name": "circuit-breaker",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "circuit-breaker-sensitivity",
"schema": {
"type": "string",
"enum": [
"strict",
"balanced",
"lenient"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "circuitBreakerMultiplier",
"transform": "identity",
"role": "body",
"input": "circuit-breaker-sensitivity",
"contributors": [
"circuit-breaker-sensitivity"
]
}
],
"valueTranslations": {
"strict": 2,
"balanced": 3,
"lenient": 5
}
},
{
"name": "destination-countries-mode",
"schema": {
"type": "string",
"enum": [
"all",
"only-selected",
"all-except-selected"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "countryPolicy.mode",
"transform": "identity",
"role": "body",
"input": "destination-countries-mode",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "destination-countries-mode",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
}
],
"valueTranslations": {
"all": "all",
"only-selected": "include",
"all-except-selected": "exclude"
}
},
{
"name": "destination-countries",
"schema": {
"type": "string",
"pattern": "^(?:[^,]*[^,\\s][^,]*)(?:,(?:[^,]*[^,\\s][^,]*))*$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "countryPolicy.countries",
"transform": "country-codes",
"role": "body",
"input": "destination-countries",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "destination-countries",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
}
],
"validation": {
"syntax": "Comma-separated values; every segment must contain at least one non-whitespace character."
}
},
{
"name": "recommended-limits",
"schema": {
"type": "boolean"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
},
"targets": [
{
"role": "body",
"path": "rateLimiting.perPipeline.perMinute",
"transform": "recommended-minute"
},
{
"role": "body",
"path": "rateLimiting.perPipeline.perHour",
"transform": "recommended-hour"
},
{
"role": "body",
"path": "rateLimiting.perPipeline.perDay",
"transform": "recommended-day"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision"
}
],
"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": "body",
"path": "rateLimiting.perPipeline.perMinute",
"transform": "recommended-minute",
"input": "recommended-limits",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
},
{
"role": "body",
"path": "rateLimiting.perPipeline.perHour",
"transform": "recommended-hour",
"input": "recommended-limits",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
},
{
"role": "body",
"path": "rateLimiting.perPipeline.perDay",
"transform": "recommended-day",
"input": "recommended-limits",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"input": "recommended-limits",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
}
]
},
{
"name": "period",
"schema": {
"type": "string",
"enum": [
"last-month",
"last-quarter",
"last-6-months"
]
},
"required": false,
"input": {
"role": "local-control",
"purpose": "recommendation-period",
"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": "period",
"inputRole": "local-control",
"transform": "identity",
"purpose": "recommendation-period",
"contributors": [
"period"
]
}
]
},
{
"name": "otp-template",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "defaultOtpTemplateId",
"transform": "default-null",
"role": "body",
"input": "otp-template",
"contributors": [
"otp-template"
]
}
]
},
{
"name": "api-version",
"schema": {
"type": "string",
"enum": [
"v1",
"v1.2",
"v2"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.apiVersion",
"transform": "identity",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
}
]
},
{
"name": "pow-starting-difficulty",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.pow.difficulty",
"transform": "adaptive-null-number",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
]
},
{
"name": "passkey-company-name",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyBranding.customCompanyName",
"transform": "none-empty",
"role": "body",
"input": "passkey-company-name",
"contributors": [
"passkey-company-name"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-company-name",
"contributors": [
"passkey-company-name"
]
}
]
},
{
"name": "passkey-logo-url",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyBranding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "passkey-logo-url",
"contributors": [
"passkey-logo-url"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-logo-url",
"contributors": [
"passkey-logo-url"
]
}
]
},
{
"name": "passkey-primary-color",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyBranding.customPrimaryColor",
"transform": "none-empty",
"role": "body",
"input": "passkey-primary-color",
"contributors": [
"passkey-primary-color"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-primary-color",
"contributors": [
"passkey-primary-color"
]
}
]
},
{
"name": "passkey-secondary-color",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyBranding.customSecondaryColor",
"transform": "none-empty",
"role": "body",
"input": "passkey-secondary-color",
"contributors": [
"passkey-secondary-color"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-secondary-color",
"contributors": [
"passkey-secondary-color"
]
}
]
},
{
"name": "passkey-theme",
"schema": {
"type": "string",
"enum": [
"light",
"dark",
"system"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyBranding.theme",
"transform": "identity",
"role": "body",
"input": "passkey-theme",
"contributors": [
"passkey-theme"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-theme",
"contributors": [
"passkey-theme"
]
}
]
},
{
"name": "turnstile",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.turnstile.enabled",
"transform": "identity",
"role": "body",
"input": "turnstile",
"contributors": [
"turnstile"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile",
"contributors": [
"turnstile"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "turnstile-allowed-domains",
"schema": {
"type": "string",
"pattern": "^(?:[^,]*[^,\\s][^,]*)(?:,(?:[^,]*[^,\\s][^,]*))*$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.turnstile.domains",
"transform": "split-trim",
"role": "body",
"input": "turnstile-allowed-domains",
"contributors": [
"turnstile-allowed-domains"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-allowed-domains",
"contributors": [
"turnstile-allowed-domains"
]
}
],
"validation": {
"syntax": "Comma-separated values; every segment must contain at least one non-whitespace character."
}
},
{
"name": "turnstile-platform",
"schema": {
"type": "string",
"enum": [
"web",
"mobile"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.turnstile.platform",
"transform": "identity",
"role": "body",
"input": "turnstile-platform",
"contributors": [
"turnstile-platform"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-platform",
"contributors": [
"turnstile-platform"
]
}
]
},
{
"name": "turnstile-site-key",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.turnstile.siteKey",
"transform": "trim",
"role": "body",
"input": "turnstile-site-key",
"contributors": [
"turnstile-site-key"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-site-key",
"contributors": [
"turnstile-site-key"
]
}
]
},
{
"name": "turnstile-test-token",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.turnstile.testToken",
"transform": "trim",
"role": "body",
"input": "turnstile-test-token",
"contributors": [
"turnstile-test-token"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-test-token",
"contributors": [
"turnstile-test-token"
]
}
]
},
{
"name": "turnstile-secret-key-file",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"contract": "secret-input",
"targets": [
{
"role": "body",
"path": "securityConfig.turnstile.secretKey",
"transform": "resolved-turnstile-secret"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision"
}
],
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"minLength": 1,
"pattern": "^\\s*\\S[^\\r\\n]*\\s*$"
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"minLength": 1,
"pattern": "^\\s*\\S[^\\r\\n]*\\s*$"
},
"fileContract": {
"format": "single-line-secret",
"maxBytes": 4096,
"regularFile": true,
"trim": true,
"reader": "secrets.js:readSecretFile",
"type": "single-line-secret",
"exclusive": false,
"mode": null,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"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": "body",
"path": "securityConfig.turnstile.secretKey",
"transform": "resolved-turnstile-secret",
"input": "turnstile-secret-key-file",
"contributors": [
"turnstile-secret-key-file"
],
"fileContract": "secret-input"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"input": "turnstile-secret-key-file",
"contributors": [
"turnstile-secret-key-file"
],
"fileContract": "secret-input"
}
]
},
{
"name": "circuit-breaker-file",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "input-file",
"contract": "widget-circuit-breaker",
"requestTargets": [
{
"role": "body",
"path": "$",
"transform": "json-fields",
"fileContract": "widget-circuit-breaker",
"resource": "widget"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline"
}
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Circuit-breaker thresholds and suspension durations",
"description": "Sparse patch. Thresholds and suspensionSeconds are positive safe integers; duration values are seconds and the CLI converts them to milliseconds before sending. Unknown keys and empty patches are refused.",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false,
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"per15Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"per5Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"maxViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"secondViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"floodThresholds.per15Minutes": {
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.per5Minutes": {
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.perMinute": {
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"suspensionSeconds.firstViolation": {
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.maxViolation": {
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.secondViolation": {
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Circuit-breaker thresholds and suspension durations",
"description": "Sparse patch. Thresholds and suspensionSeconds are positive safe integers; duration values are seconds and the CLI converts them to milliseconds before sending. Unknown keys and empty patches are refused.",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false,
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
}
},
"schemaAppliesWhen": "Use the attached-widget variant; V1.2 pipelines refuse this widget-only flag.",
"variants": [
{
"command": "widgets update",
"when": [
"pipeline apiVersion is v1 or v2; v1.2 refuses widget-only settings"
],
"fileContract": {
"name": "widget-circuit-breaker",
"definition": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"floodThresholds.per15Minutes": {
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.per5Minutes": {
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.perMinute": {
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"suspensionSeconds.firstViolation": {
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.maxViolation": {
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.secondViolation": {
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"per15Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"per5Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"maxViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"secondViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"semanticSchema": {
"$ref": "#/$defs/widgetBreaker",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"widgetBreaker": {
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"wire": {
"method": "PUT",
"route": "/widgets/:id",
"query": {},
"body": {
"name": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
],
"description": [
{
"path": "description",
"transform": "none-empty",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
],
"pipeline-id": [
{
"path": "pipelineId",
"transform": "none-null",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"status": [
{
"path": "status",
"transform": "identity",
"role": "body",
"input": "status",
"contributors": [
"status"
]
}
],
"logo": [
{
"path": "branding.logo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
},
{
"path": "branding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
}
],
"company-name": [
{
"path": "branding.companyName",
"transform": "none-empty",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
},
{
"path": "branding.customCompanyName",
"transform": "company-name-mirror",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
}
],
"favicon-url": [
{
"path": "branding.faviconUrl",
"transform": "none-empty",
"role": "body",
"input": "favicon-url",
"contributors": [
"favicon-url"
]
}
],
"widget-language": [
{
"path": "branding.appearance.language",
"transform": "identity",
"role": "body",
"input": "widget-language",
"contributors": [
"widget-language"
]
}
],
"primary-color": [
{
"path": "branding.appearance.theme.colors.primary",
"transform": "none-empty",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
},
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
}
],
"secondary-color": [
{
"path": "branding.appearance.theme.colors.secondary",
"transform": "none-empty",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
},
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
}
],
"dev-mode": [
{
"path": "devMode.enabled",
"transform": "identity",
"role": "body",
"input": "dev-mode",
"contributors": [
"dev-mode"
]
}
],
"dev-mode-digits": [
{
"path": "devMode.digits",
"transform": "identity",
"role": "body",
"input": "dev-mode-digits",
"contributors": [
"dev-mode-digits"
]
}
],
"circuit-breaker": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
}
],
"pow": [
{
"path": "security.pow.allowed",
"transform": "identity",
"role": "body",
"input": "pow",
"contributors": [
"pow"
]
}
],
"pow-trigger-percent": [
{
"path": "security.pow.triggerPercent",
"transform": "identity",
"role": "body",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
}
],
"pow-starting-difficulty": [
{
"path": "security.pow.startDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
],
"pow-maximum-difficulty": [
{
"path": "security.pow.maxDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
}
],
"smart-routing": [
{
"path": "smartRouting",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"passkeys": [
{
"path": "passkeyEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"cluster-gate": [
{
"path": "securityConfig.clusterGate.enabled",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-position": [
{
"path": "securityConfig.clusterGate.position",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-escalation": [
{
"path": "securityConfig.clusterGate.escalation",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-home-countries": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"otp-language": [
{
"path": "locale",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
],
"channel-choice": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"channel-choice-mode": [
{
"path": "channelChoiceMode",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"channel-choice-enforcement": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
]
},
"ownerParams": [],
"flag": [
{
"role": "local",
"input": "circuit-breaker-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"contributors": [
"circuit-breaker-file"
]
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.per15Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.per5Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.firstViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.maxViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.secondViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"fileContract": "widget-circuit-breaker"
}
]
}
}
],
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "circuit-breaker-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"contributors": [
"circuit-breaker-file"
]
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"resource": "widget",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.per15Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"resource": "widget",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.per5Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"resource": "widget",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"resource": "widget",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.firstViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"resource": "widget",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.maxViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"resource": "widget",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.secondViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"fileContract": "widget-circuit-breaker"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"fileContract": "widget-circuit-breaker"
}
]
},
{
"name": "pow",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"resource": "widget",
"path": "security.pow.allowed",
"transform": "identity"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget"
}
],
"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": "body",
"resource": "widget",
"path": "security.pow.allowed",
"transform": "identity",
"input": "pow",
"contributors": [
"pow"
]
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"input": "pow",
"contributors": [
"pow"
]
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"input": "pow",
"contributors": [
"pow"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "pow-trigger-percent",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:(?:2[5-9]|[3-8]\\d{1}|9[0-5]))(?:\\.0+)?)$"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"resource": "widget",
"path": "security.pow.triggerPercent",
"transform": "identity"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget"
}
],
"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": "body",
"resource": "widget",
"path": "security.pow.triggerPercent",
"transform": "identity",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 25,
"maximum": 95,
"parsedValueSchema": {
"type": "integer",
"minimum": 25,
"maximum": 95
}
}
},
{
"name": "pow-maximum-difficulty",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[3-6])(?:\\.0+)?)$"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"resource": "widget",
"path": "security.pow.maxDifficulty",
"transform": "identity"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget"
}
],
"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": "body",
"resource": "widget",
"path": "security.pow.maxDifficulty",
"transform": "identity",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 3,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 3,
"maximum": 6
}
}
}
],
"files": [
{
"name": "pipeline-rate-limits",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"perPhoneNumber.perDay": {
"path": "rateLimiting.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"perPhoneNumber.perHour": {
"path": "rateLimiting.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"perPhoneNumber.perMinute": {
"path": "rateLimiting.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"perIpAddress.perDay": {
"path": "rateLimiting.perEndUserIP.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"perIpAddress.perHour": {
"path": "rateLimiting.perEndUserIP.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"perIpAddress.perMinute": {
"path": "rateLimiting.perEndUserIP.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"perPipeline.perDay": {
"path": "rateLimiting.perPipeline.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"perPipeline.perHour": {
"path": "rateLimiting.perPipeline.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"perPipeline.perMinute": {
"path": "rateLimiting.perPipeline.perMinute",
"transform": "identity",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "pipeline"
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perIpAddress": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPipeline": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"uses": [
{
"flag": "rate-limits-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Pipeline rate-limit patch",
"description": "Sparse patch in CLI file-key form. Only listed limits change. Numbers are positive safe integers; unknown keys, dotted keys, empty nested objects and an empty root object are refused. perIpAddress is mapped by the CLI to the API's perEndUserIP.",
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/limits"
},
"perIpAddress": {
"$ref": "#/$defs/limits"
},
"perPipeline": {
"$ref": "#/$defs/limits"
}
},
"minProperties": 1,
"additionalProperties": false,
"$defs": {
"limits": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
}
}
}
]
},
{
"name": "secret-input",
"contract": {
"format": "single-line-secret",
"maxBytes": 4096,
"regularFile": true,
"trim": true,
"reader": "secrets.js:readSecretFile",
"type": "single-line-secret",
"exclusive": false,
"mode": null,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"minLength": 1,
"pattern": "^\\s*\\S[^\\r\\n]*\\s*$"
},
"uses": [
{
"flag": "turnstile-secret-key-file",
"role": "handler-built",
"purpose": null
}
]
},
{
"name": "widget-circuit-breaker",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"floodThresholds.per15Minutes": {
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.per5Minutes": {
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.perMinute": {
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"suspensionSeconds.firstViolation": {
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.maxViolation": {
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.secondViolation": {
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"per15Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"per5Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"maxViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"secondViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"uses": [
{
"flag": "circuit-breaker-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Circuit-breaker thresholds and suspension durations",
"description": "Sparse patch. Thresholds and suspensionSeconds are positive safe integers; duration values are seconds and the CLI converts them to milliseconds before sending. Unknown keys and empty patches are refused.",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false,
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
}
}
}
]
}
],
"wire": {
"method": "PUT",
"route": "/pipelines/id/:id",
"query": {},
"body": {
"name": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
],
"description": [
{
"path": "description",
"transform": "identity",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
],
"status": [
{
"path": "active",
"transform": "identity",
"role": "body",
"input": "status",
"contributors": [
"status"
]
}
],
"environment": [
{
"path": "type",
"transform": "identity",
"role": "body",
"input": "environment",
"contributors": [
"environment"
]
}
],
"otp-language": [
{
"path": "locale",
"transform": "identity",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
],
"redirect-url": [
{
"path": "frontendCallbackURL",
"transform": "none-empty",
"role": "body",
"input": "redirect-url",
"contributors": [
"redirect-url"
]
}
],
"webhook-url": [
{
"path": "backendCallbackURL",
"transform": "none-empty",
"role": "body",
"input": "webhook-url",
"contributors": [
"webhook-url"
]
}
],
"smart-routing": [
{
"path": "smartRouting",
"transform": "identity",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"passkeys": [
{
"path": "passkeyEnabled",
"transform": "identity",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"zero-verify-bans": [
{
"path": "zeroVerifyBansEnabled",
"transform": "identity",
"role": "body",
"input": "zero-verify-bans",
"contributors": [
"zero-verify-bans"
]
}
],
"byo-sms": [
{
"path": "byoSms",
"transform": "identity",
"role": "body",
"input": "byo-sms",
"contributors": [
"byo-sms"
]
}
],
"cluster-gate": [
{
"path": "securityConfig.clusterGate.enabled",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-position": [
{
"path": "securityConfig.clusterGate.position",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-escalation": [
{
"path": "securityConfig.clusterGate.escalation",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-home-countries": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"method-order": [
{
"path": "useMethodOrder",
"transform": "identity",
"role": "body",
"input": "method-order",
"contributors": [
"method-order"
]
}
],
"channel-choice": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"channel-choice-mode": [
{
"path": "channelChoiceMode",
"transform": "identity",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"channel-choice-enforcement": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
],
"circuit-breaker": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker"
]
}
],
"circuit-breaker-sensitivity": [
{
"path": "circuitBreakerMultiplier",
"transform": "identity",
"role": "body",
"input": "circuit-breaker-sensitivity",
"contributors": [
"circuit-breaker-sensitivity"
]
}
],
"destination-countries-mode": [
{
"path": "countryPolicy.mode",
"transform": "identity",
"role": "body",
"input": "destination-countries-mode",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "destination-countries-mode",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
}
],
"destination-countries": [
{
"path": "countryPolicy.countries",
"transform": "country-codes",
"role": "body",
"input": "destination-countries",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "destination-countries",
"contributors": [
"destination-countries",
"destination-countries-mode",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"recommended": "recommended-limits",
"mode": "destination-countries-mode",
"countries": "destination-countries"
}
}
],
"otp-template": [
{
"path": "defaultOtpTemplateId",
"transform": "default-null",
"role": "body",
"input": "otp-template",
"contributors": [
"otp-template"
]
}
],
"api-version": [
{
"path": "securityConfig.apiVersion",
"transform": "identity",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
}
],
"pow-starting-difficulty": [
{
"path": "securityConfig.pow.difficulty",
"transform": "adaptive-null-number",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
],
"passkey-company-name": [
{
"path": "passkeyBranding.customCompanyName",
"transform": "none-empty",
"role": "body",
"input": "passkey-company-name",
"contributors": [
"passkey-company-name"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-company-name",
"contributors": [
"passkey-company-name"
]
}
],
"passkey-logo-url": [
{
"path": "passkeyBranding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "passkey-logo-url",
"contributors": [
"passkey-logo-url"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-logo-url",
"contributors": [
"passkey-logo-url"
]
}
],
"passkey-primary-color": [
{
"path": "passkeyBranding.customPrimaryColor",
"transform": "none-empty",
"role": "body",
"input": "passkey-primary-color",
"contributors": [
"passkey-primary-color"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-primary-color",
"contributors": [
"passkey-primary-color"
]
}
],
"passkey-secondary-color": [
{
"path": "passkeyBranding.customSecondaryColor",
"transform": "none-empty",
"role": "body",
"input": "passkey-secondary-color",
"contributors": [
"passkey-secondary-color"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-secondary-color",
"contributors": [
"passkey-secondary-color"
]
}
],
"passkey-theme": [
{
"path": "passkeyBranding.theme",
"transform": "identity",
"role": "body",
"input": "passkey-theme",
"contributors": [
"passkey-theme"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "passkey-theme",
"contributors": [
"passkey-theme"
]
}
],
"turnstile": [
{
"path": "securityConfig.turnstile.enabled",
"transform": "identity",
"role": "body",
"input": "turnstile",
"contributors": [
"turnstile"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile",
"contributors": [
"turnstile"
]
}
],
"turnstile-allowed-domains": [
{
"path": "securityConfig.turnstile.domains",
"transform": "split-trim",
"role": "body",
"input": "turnstile-allowed-domains",
"contributors": [
"turnstile-allowed-domains"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-allowed-domains",
"contributors": [
"turnstile-allowed-domains"
]
}
],
"turnstile-platform": [
{
"path": "securityConfig.turnstile.platform",
"transform": "identity",
"role": "body",
"input": "turnstile-platform",
"contributors": [
"turnstile-platform"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-platform",
"contributors": [
"turnstile-platform"
]
}
],
"turnstile-site-key": [
{
"path": "securityConfig.turnstile.siteKey",
"transform": "trim",
"role": "body",
"input": "turnstile-site-key",
"contributors": [
"turnstile-site-key"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-site-key",
"contributors": [
"turnstile-site-key"
]
}
],
"turnstile-test-token": [
{
"path": "securityConfig.turnstile.testToken",
"transform": "trim",
"role": "body",
"input": "turnstile-test-token",
"contributors": [
"turnstile-test-token"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"role": "body",
"input": "turnstile-test-token",
"contributors": [
"turnstile-test-token"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
Protected linked-resource writes can be partial; honor PARTIAL_UPDATE details and inspect each named resource before retrying.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"updatedAt": {
"type": "string"
},
"active": {
"type": "boolean"
},
"type": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "A later linked write cannot be confirmed",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"code": {
"const": "PARTIAL_UPDATE"
},
"message": {
"type": "string"
},
"details": {
"type": "object",
"properties": {
"applied": {
"type": "array",
"items": {
"type": "object",
"properties": {
"resource": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [
"resource",
"id"
],
"additionalProperties": true
}
},
"notApplied": {
"type": "array",
"items": {
"type": "object",
"properties": {
"resource": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [
"resource",
"id"
],
"additionalProperties": true
}
},
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
}
},
"required": [
"code",
"httpStatus"
],
"additionalProperties": true
}
},
"required": [
"applied",
"notApplied",
"error"
],
"additionalProperties": true
},
"hint": {
"type": "string"
}
},
"required": [
"code",
"message",
"details",
"hint"
],
"additionalProperties": true
},
"notes": [
"Inspect applied and notApplied resources. A rerun may repeat a write that completed without confirmation."
],
"title": "A later linked write cannot be confirmed"
},
{
"when": "--dry-run when linked settings require multiple resource writes",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
}
}
},
"required": [
"steps"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"Linked planning returns status:\"success\" and data.steps. Planning reads may occur; none of the listed writes is executed."
],
"title": "--dry-run when linked settings require multiple resource writes"
},
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "PUT",
"url": "https://api.example.invalid/api/v1/pipelines/id/0123456789abcdef01234567",
"body": {
"name": "Example OTP"
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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 successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
}
]
{
"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": {
"TEMPLATE_NOT_APPROVED": "Run akedly whatsapp otp-templates list and check approval for the pipeline language before attaching the template.",
"TEMPLATE_PIPELINE_REQUIRES_WHATSAPP": "Enable WhatsApp with --verification-methods before attaching a custom OTP template, or use --otp-template default.",
"API_VERSION_CHANGE_NOT_ALLOWED": "Only V1.2 to V2 is supported. Keep the current API version or create a separate pipeline for another version. V2 requires both --redirect-url and --webhook-url.",
"PIPELINE_POW_INVALID": "Use --pow-starting-difficulty adaptive or a whole number from 2 to 7. This sets the starting difficulty; Akedly controls whether pipeline PoW is enabled.",
"PASSKEY_BRANDING_INVALID": "Use an HTTPS logo, a company name of at most 100 characters, six-digit #RRGGBB colours and light, dark or system for the theme. Use none to restore a company default.",
"TURNSTILE_DOMAINS_NOT_EDITABLE": "--turnstile-allowed-domains is only for an enabled existing legacy Web integration. Manage customer-owned integration hostnames in Cloudflare."
},
"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
}
}
Own recovery guidance
| Code | Hint |
|---|---|
| TEMPLATE_NOT_APPROVED | Run akedly whatsapp otp-templates list and check approval for the pipeline language before attaching the template. |
| TEMPLATE_PIPELINE_REQUIRES_WHATSAPP | Enable WhatsApp with --verification-methods before attaching a custom OTP template, or use --otp-template default. |
| API_VERSION_CHANGE_NOT_ALLOWED | Only V1.2 to V2 is supported. Keep the current API version or create a separate pipeline for another version. V2 requires both --redirect-url and --webhook-url. |
| PIPELINE_POW_INVALID | Use --pow-starting-difficulty adaptive or a whole number from 2 to 7. This sets the starting difficulty; Akedly controls whether pipeline PoW is enabled. |
| PASSKEY_BRANDING_INVALID | Use an HTTPS logo, a company name of at most 100 characters, six-digit #RRGGBB colours and light, dark or system for the theme. Use none to restore a company default. |
| TURNSTILE_DOMAINS_NOT_EDITABLE | --turnstile-allowed-domains is only for an enabled existing legacy Web integration. Manage customer-owned integration hostnames in Cloudflare. |
pipelines delete
Remove a pipeline that should no longer accept verification traffic.
When to use
- Use only after confirming no application or widget depends on it.
How it works
- Requests deletion after explicit confirmation; dry-run reports the planned operation without deleting.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
The pipeline is deleted; the response contains the deleted pipeline under data and a confirmation message at the root.
Next steps
- Remove references from applications and any widgets before running this irreversible action.
Usage
akedly pipelines delete <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines delete" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--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: pipelines:delete.
Server request: DELETE /pipelines/id/:id.
Consent
--yes. --dry-run bypasses confirmation where supported. Destructive: true. Spends money: false. Real send: false.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
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
Delete a pipeline. Read it first to check hasAttachedWidget; deletion has no attachment check.
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.
Synthetic pipelines delete workflow
Remove a pipeline that should no longer accept verification traffic.
akedly pipelines delete 0123456789abcdef01234567 --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"securityConfig": {
"apiVersion": "v1.2"
}
},
"message": "Pipeline deleted successfully"
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines delete <id> --yes
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "DELETE",
"route": "/pipelines/id/:id",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"active": {
"type": "boolean"
},
"type": {
"type": "string"
},
"securityConfig": {
"type": "object",
"properties": {
"apiVersion": {
"type": "string"
}
},
"additionalProperties": true
}
},
"required": [
"_id",
"active",
"type"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data",
"message"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "DELETE",
"url": "https://api.example.invalid/api/v1/pipelines/id/0123456789abcdef01234567"
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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 successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"securityConfig": {
"apiVersion": "v1.2"
}
},
"message": "Pipeline deleted successfully"
}
}
]
{
"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
}
}
pipelines billing-status
Check the billing eligibility and status information used when deciding whether a pipeline can process paid verification traffic.
When to use
- Use when a pipeline send is blocked by billing or during account setup.
How it works
- Reads the account billing-status endpoint.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Server billing-status object.
Next steps
- Resolve any account billing requirement shown by the response before sending.
Usage
akedly pipelines billing-status
Inspect this command offline before preparing an operation:
akedly describe "pipelines billing-status" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--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: pipelines:read.
Server request: GET /pipelines/billing-status/:userID.
Owner parameters resolved from the CLI Login: userID.
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
Read billing status for your account pipelines.
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.
Synthetic pipelines billing-status workflow
Check the billing eligibility and status information used when deciding whether a pipeline can process paid verification traffic.
akedly pipelines billing-status
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"user": {
"_id": "0123456789abcdef01234567",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "user"
},
"billingPlan": {
"_id": "89abcdef0123456789abcdef",
"name": "Example plan",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
},
"pipelines": [
{
"_id": "fedcba987654321001234567",
"name": "Example OTP",
"billingMode": null,
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
}
]
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines billing-status
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
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/billing-status/:userID",
"query": {},
"body": {},
"ownerParams": [
"userID"
]
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"pipelines": {
"type": "array"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"user": {
"_id": "0123456789abcdef01234567",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "user"
},
"billingPlan": {
"_id": "89abcdef0123456789abcdef",
"name": "Example plan",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
},
"pipelines": [
{
"_id": "fedcba987654321001234567",
"name": "Example OTP",
"billingMode": null,
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
}
]
}
}
}
]
{
"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
}
}
pipelines active-billing-status
Check billing readiness for currently active pipelines before a rollout.
When to use
- Use when confirming which active pipelines have usable billing status.
How it works
- Reads the active-pipeline billing summary.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Server billing status summary.
Next steps
- Review inactive or unready pipelines separately with pipelines get and billing-status.
Usage
akedly pipelines active-billing-status
Inspect this command offline before preparing an operation:
akedly describe "pipelines active-billing-status" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--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: pipelines:read.
Server request: GET /pipelines/active-billing-status/:userID.
Owner parameters resolved from the CLI Login: userID.
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
Read billing status for the first active live pipeline.
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.
Synthetic pipelines active-billing-status workflow
Check billing readiness for currently active pipelines before a rollout.
akedly pipelines active-billing-status
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"user": {
"_id": "0123456789abcdef01234567",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "user"
},
"billingPlan": {
"_id": "89abcdef0123456789abcdef",
"name": "Example plan",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
},
"pipeline": {
"_id": "fedcba987654321001234567",
"name": "Example OTP",
"active": true,
"type": "Live",
"billingMode": null,
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines active-billing-status
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
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/active-billing-status/:userID",
"query": {},
"body": {},
"ownerParams": [
"userID"
]
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"pipelines": {
"type": "array"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"user": {
"_id": "0123456789abcdef01234567",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "user"
},
"billingPlan": {
"_id": "89abcdef0123456789abcdef",
"name": "Example plan",
"billingMode": "PPM",
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
},
"pipeline": {
"_id": "fedcba987654321001234567",
"name": "Example OTP",
"active": true,
"type": "Live",
"billingMode": null,
"resolvedBillingMode": "PPM",
"resolvedSource": "plan"
}
}
}
}
]
{
"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
}
}
pipelines otp-sms-preview
Preview the actual locale-specific OTP copy and SMS segmentation before configuring an OTP pipeline.
When to use
- Use to inspect English or Arabic OTP text without sending a message.
How it works
- Renders a synthetic zero-filled code using the configured locale and digit count, applies the current company name or fallback, and returns encoding, characters, segments, and truncation details. It sends no SMS.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Rendered preview fields returned by the API.
Next steps
- Review wording and length before enabling the corresponding pipeline settings.
Usage
akedly pipelines otp-sms-preview
Inspect this command offline before preparing an operation:
akedly describe "pipelines otp-sms-preview" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--otp-language | enum | en | ar | Command-specific option; see the full command details below. Query parameter: locale. |
--digits | integer | 4 to 6 | Command-specific option; see the full command details below. Query parameter: digits. |
--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: pipelines:read.
Server request: GET /pipelines/otp-sms-preview.
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
Preview OTP SMS text without sending a message.
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.
Synthetic pipelines otp-sms-preview workflow
Preview the localized OTP SMS copy and digit count before choosing customer-facing message settings.
akedly pipelines otp-sms-preview --otp-language en --digits 6
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"locale": "en",
"otpDigits": 6,
"appliesTo": "cequens",
"wouldTruncate": false,
"segments": 1,
"encoding": "GSM7",
"characters": 38,
"companyName": "Example Co",
"sample": "000000 is your OTP. Sent by Example Co"
},
"message": "OTP SMS preview generated successfully"
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines otp-sms-preview --otp-language en --digits 4
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": "otp-language",
"schema": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"required": false,
"wire": [
{
"path": "locale",
"transform": "identity",
"role": "query",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
]
},
{
"name": "digits",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[4-6])(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "digits",
"transform": "identity",
"role": "query",
"input": "digits",
"contributors": [
"digits"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 4,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 4,
"maximum": 6
}
}
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/otp-sms-preview",
"query": {
"otp-language": [
{
"path": "locale",
"transform": "identity",
"role": "query",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
],
"digits": [
{
"path": "digits",
"transform": "identity",
"role": "query",
"input": "digits",
"contributors": [
"digits"
]
}
]
},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"locale": {
"type": "string"
},
"otpDigits": {
"type": "integer"
},
"appliesTo": {
"type": "string"
},
"wouldTruncate": {
"type": "boolean"
},
"segments": {
"type": "integer"
},
"encoding": {
"type": "string"
},
"characters": {
"type": "integer"
},
"companyName": {
"type": "string"
},
"sample": {
"type": "string"
}
},
"required": [
"locale",
"otpDigits",
"appliesTo",
"wouldTruncate",
"segments",
"encoding",
"characters",
"companyName",
"sample"
],
"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 successful response",
"value": {
"status": "success",
"data": {
"locale": "en",
"otpDigits": 6,
"appliesTo": "cequens",
"wouldTruncate": false,
"segments": 1,
"encoding": "GSM7",
"characters": 38,
"companyName": "Example Co",
"sample": "000000 is your OTP. Sent by Example Co"
},
"message": "OTP SMS preview generated successfully"
}
}
]
{
"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
}
}
pipelines pow-policy
See the server’s current proof-of-work policy before configuring abuse protection.
When to use
- Use when deciding whether PoW thresholds comply with server policy.
How it works
- Reads the policy endpoint without changing a pipeline.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Policy values returned by the server.
Next steps
- Use the reported limits when selecting PoW settings.
Usage
akedly pipelines pow-policy
Inspect this command offline before preparing an operation:
akedly describe "pipelines pow-policy" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--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: pipelines:read.
Server request: GET /pipelines/pow-policy.
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
Read the proof-of-work policy.
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.
Synthetic pipelines pow-policy workflow
See the server’s current proof-of-work policy before configuring abuse protection.
akedly pipelines pow-policy
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"minDifficulty": 3,
"defaultDifficulty": 4,
"maxDifficulty": 6
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines pow-policy
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
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/pow-policy",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"minDifficulty": {
"type": "integer"
},
"defaultDifficulty": {
"type": "integer"
},
"maxDifficulty": {
"type": "integer"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"minDifficulty": 3,
"defaultDifficulty": 4,
"maxDifficulty": 6
}
}
}
]
{
"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
}
}
pipelines rate-recommendations
Estimate safer pipeline rate limits from observed traffic before applying limits.
When to use
- Use when tuning an owned non-system pipeline; recommendations require enough analysed traffic and confidence.
How it works
- Requests an analysis for the selected period or custom date range; this command only returns recommendations.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Analysis result, including readiness/confidence and suggested settings when available.
Next steps
- Apply only after reviewing recommendations with pipelines update; low traffic returns no ready settings.
Usage
akedly pipelines rate-recommendations <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines rate-recommendations" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--period | enum | last-month | last-quarter | last-6-months | Command-specific option; see the full command details below. Manifest body field: period. |
--start-date | text | — | Command-specific option; see the full command details below. Manifest body field: customDateRange.sDate. |
--end-date | text | — | Command-specific option; see the full command details below. Manifest body field: customDateRange.eDate. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--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: pipelines:update.
Server request: POST /pipeline-rate-recommendations/:pipelineID.
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
Read rate recommendations. Requires Full access; dates use server-local-day semantics.
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.
Synthetic pipelines rate-recommendations workflow
Estimate safer pipeline rate limits from observed traffic before applying limits.
akedly pipelines rate-recommendations 0123456789abcdef01234567 --period last-month
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"applyReady": {
"enoughTraffic": true,
"confidence": "high",
"analysedRequests": 250,
"settings": {
"rateLimiting": {
"perPipeline": {
"perMinute": 30,
"perHour": 600,
"perDay": 3000
}
}
}
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines rate-recommendations <id> --period last-month
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "pipelineID",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "period",
"schema": {
"type": "string",
"enum": [
"last-month",
"last-quarter",
"last-6-months"
]
},
"required": false,
"wire": [
{
"path": "period",
"transform": "identity",
"role": "body",
"input": "period",
"contributors": [
"period"
]
}
],
"valueTranslations": {
"last-month": "last_month",
"last-quarter": "last_quarter",
"last-6-months": "last_6_months"
}
},
{
"name": "start-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "customDateRange.sDate",
"transform": "identity",
"role": "body",
"input": "start-date",
"contributors": [
"start-date"
]
}
]
},
{
"name": "end-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "customDateRange.eDate",
"transform": "identity",
"role": "body",
"input": "end-date",
"contributors": [
"end-date"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "POST",
"route": "/pipeline-rate-recommendations/:pipelineID",
"query": {},
"body": {
"period": [
{
"path": "period",
"transform": "identity",
"role": "body",
"input": "period",
"contributors": [
"period"
]
}
],
"start-date": [
{
"path": "customDateRange.sDate",
"transform": "identity",
"role": "body",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"end-date": [
{
"path": "customDateRange.eDate",
"transform": "identity",
"role": "body",
"input": "end-date",
"contributors": [
"end-date"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"applyReady": {
"type": "object"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"applyReady": {
"enoughTraffic": true,
"confidence": "high",
"analysedRequests": 250,
"settings": {
"rateLimiting": {
"perPipeline": {
"perMinute": 30,
"perHour": 600,
"perDay": 3000
}
}
}
}
}
}
}
]
{
"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
}
}
pipelines dev-mode show
Inspect whether a pipeline currently has the development test-pair mode and bypasses enabled.
When to use
- Use before running a safe development test pair or diagnosing why a test bypass is unavailable.
How it works
- Reads dev-mode configuration for the pipeline.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Current dev-mode settings.
Next steps
- Use dev-mode set to change settings; test-pair flows are synthetic and are not production verification.
Usage
akedly pipelines dev-mode show <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines dev-mode show" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--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: pipelines:dev_mode.
Server request: GET /pipelines/id/:id/dev-mode.
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
Read dev mode and its disposable Test Pair. Requires Full access.
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.
Synthetic pipelines dev-mode show workflow
Inspect whether a pipeline currently has the development test-pair mode and bypasses enabled.
akedly pipelines dev-mode show 0123456789abcdef01234567
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"enabled": false
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines dev-mode show <id>
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/id/:id/dev-mode",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"devMode": {
"type": "boolean"
},
"bypassTurnstile": {
"type": "boolean"
},
"bypassPoW": {
"type": "boolean"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"enabled": false
}
}
}
]
{
"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
}
}
pipelines dev-mode set
Enable or disable the pipeline’s development test-pair behavior and permitted Turnstile/PoW bypasses.
When to use
- Use for controlled development testing after confirming the pipeline is the intended test pipeline.
How it works
- Requires confirmation for writes; dry-run previews. Bypasses only affect eligible development test-pair traffic.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Saved development-mode settings.
Next steps
- Run a test-pair flow and turn development mode off when finished.
Usage
akedly pipelines dev-mode set <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines dev-mode set" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--dev-mode | enum | on | off | yes | Command-specific option; see the full command details below. Manifest body field: enabled. Tip: Do not leave dev mode on for production traffic: while the window is open, anyone who knows the test number and code can complete a verification for that number. |
--bypass-turnstile | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: bypassTurnstile. Tip: On V1.2 pipelines, use Bypass Turnstile for testing from localhost instead of adding localhost in Cloudflare: when it is on, while the dev-mode window is open, it skips Turnstile for every request to the pipeline, so never turn it on for a pipeline serving production traffic. |
--bypass-pow | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: bypassPoW. Tip: On V1.2 pipelines, keep Bypass PoW off unless proof-of-work blocks your tests: when it is on, while the dev-mode window is open, it skips the check for every request to the pipeline, not only the test number. It stops working when the window ends or dev mode is turned off. |
--dev-mode-digits | integer | 4 to 6 | no | Command-specific option; see the full command details below. Manifest body field: digits. |
--dry-run | boolean switch | — | no | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--yes | boolean switch | — | no | Confirm this action where consent is required. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: pipelines:dev_mode.
Server request: PUT /pipelines/id/:id/dev-mode.
Consent
--yes. --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
Set the one-hour dev-mode window for V1.2. Enabling while already on does not extend the hour; disable and re-enable for a fresh hour and a new Test Pair. When enabling, omitted bypass flags reset to off. The server returns a disposable Test Pair without sending anything. Dev mode loosens live verification on this pipeline (it bypasses rate limiting and circuit breaker), so every change here needs --yes, turning it off included; --dry-run previews without --yes.
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.
Synthetic pipelines dev-mode set workflow
Enable or disable the pipeline’s development test-pair behavior and permitted Turnstile/PoW bypasses.
akedly pipelines dev-mode set 0123456789abcdef01234567 --dev-mode on --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"devMode": {
"enabled": true,
"bypassTurnstile": true,
"bypassPoW": true
},
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines dev-mode set <id> --dev-mode on --dev-mode-digits 4 --yes
akedly pipelines dev-mode set <id> --dev-mode off --yes
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "dev-mode",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": true,
"requiresConsent": true,
"wire": [
{
"path": "enabled",
"transform": "identity",
"role": "body",
"input": "dev-mode",
"contributors": [
"dev-mode"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "bypass-turnstile",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "bypassTurnstile",
"transform": "identity",
"role": "body",
"input": "bypass-turnstile",
"contributors": [
"bypass-turnstile"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "bypass-pow",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "bypassPoW",
"transform": "identity",
"role": "body",
"input": "bypass-pow",
"contributors": [
"bypass-pow"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "dev-mode-digits",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[4-6])(?:\\.0+)?)$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "digits",
"transform": "identity",
"role": "body",
"input": "dev-mode-digits",
"contributors": [
"dev-mode-digits"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 4,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 4,
"maximum": 6
}
}
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "confirmation",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "confirmation",
"contributors": [
"yes"
]
}
]
}
],
"files": [],
"wire": {
"method": "PUT",
"route": "/pipelines/id/:id/dev-mode",
"query": {},
"body": {
"dev-mode": [
{
"path": "enabled",
"transform": "identity",
"role": "body",
"input": "dev-mode",
"contributors": [
"dev-mode"
]
}
],
"bypass-turnstile": [
{
"path": "bypassTurnstile",
"transform": "identity",
"role": "body",
"input": "bypass-turnstile",
"contributors": [
"bypass-turnstile"
]
}
],
"bypass-pow": [
{
"path": "bypassPoW",
"transform": "identity",
"role": "body",
"input": "bypass-pow",
"contributors": [
"bypass-pow"
]
}
],
"dev-mode-digits": [
{
"path": "digits",
"transform": "identity",
"role": "body",
"input": "dev-mode-digits",
"contributors": [
"dev-mode-digits"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"devMode": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"bypassTurnstile": {
"type": "boolean"
},
"bypassPoW": {
"type": "boolean"
}
},
"additionalProperties": true
},
"bypassTurnstile": {
"type": "boolean"
},
"bypassPoW": {
"type": "boolean"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "PUT",
"url": "https://api.example.invalid/api/v1/pipelines/id/0123456789abcdef01234567/dev-mode",
"body": {
"enabled": true
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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 successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example OTP",
"active": false,
"type": "Test",
"devMode": {
"enabled": true,
"bypassTurnstile": true,
"bypassPoW": true
},
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
}
]
{
"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
}
}
pipelines webhook-secret reveal
Recover the current webhook signing secret while it remains unacknowledged; the reveal operation can be repeated until acknowledgement.
When to use
- Use before acknowledgement to recover the current secret when needed; repeated reveal calls continue to return it until acknowledged.
How it works
- Reads the current secret. Reveal does not consume or acknowledge it; only the acknowledgement command closes this reveal window. With
--secret-file, the CLI removes the secret field from JSON and writes it privately; without it, JSON contains the secret.
Prerequisites
- A CLI login with permission for this account and resource.
- For secret-safe machine output, choose a new private
--secret-filepath; omitting it places webhookSigningSecret in the JSON response.
Expected outcome
The current webhook signing secret. A private file sink returns secretFile without the secret; without --secret-file the secret is present in JSON.
Next steps
- Store the file in a secret manager; do not paste it into logs.
Usage
akedly pipelines webhook-secret reveal <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines webhook-secret reveal" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--secret-file | text | — | Write the secret to a new mode 0600 file and omit it from output; an existing path is refused. |
--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: pipelines:update.
Server request: GET /pipelines/id/:id/webhook-secret.
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
Sensitive output: secret-sink. Use the supported sink; default output may contain a secret. Supported sinks: --secret-file.
Full command details
Reveal the webhook signing secret until acknowledged. Use --secret-file to suppress it from output.
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.
Synthetic pipelines webhook-secret reveal workflow
Recover the current unacknowledged secret and write it to a private file; the reveal can be repeated until acknowledged.
akedly pipelines webhook-secret reveal 0123456789abcdef01234567 --secret-file ./private/webhook-secret.txt
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"secretFile": "./private/webhook-secret.txt"
},
"message": "Copy this secret now — it will not be shown again until you regenerate"
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Secret output sink: ./private/webhook-secret.txt. Use a new private path when running the command; the synthetic example is documentation only.
Additional command forms
akedly pipelines webhook-secret reveal <id> --secret-file <new path>
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "secret-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "output-sink",
"contract": "secret-output",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"fileContract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"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": "secret-file",
"inputRole": "output-sink",
"transform": "identity",
"fileContract": "secret-output",
"contributors": [
"secret-file"
]
}
]
}
],
"files": [
{
"name": "secret-output",
"contract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"uses": [
{
"flag": "secret-file",
"role": "output-sink",
"purpose": null
}
]
}
],
"wire": {
"method": "GET",
"route": "/pipelines/id/:id/webhook-secret",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
Without --secret-file, the CLI passes through the webhookSigningSecret in JSON. Use a private --secret-file to keep secret bytes out of stdout.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"webhookSigningSecret": {
"type": "string",
"pattern": "^whsec_[A-Za-z0-9+/]{43}=$"
},
"secretFile": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--secret-file receives one-time secret delivery",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"secretFile": {
"type": "string"
}
},
"required": [
"secretFile"
],
"not": {
"required": [
"webhookSigningSecret"
]
},
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"With --secret-file, secretDelivery removes data.webhookSigningSecret and adds data.secretFile. The secret bytes are written to the selected file."
],
"title": "--secret-file receives one-time secret delivery"
},
{
"when": "--secret-file is omitted",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"webhookSigningSecret": {
"type": "string",
"pattern": "^whsec_[A-Za-z0-9+/]{43}=$"
},
"secretFile": {
"type": "string"
}
},
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"The CLI passes through data.webhookSigningSecret in JSON output. Treat stdout as secret material; do not log or expose it."
],
"title": "--secret-file is omitted"
}
],
"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": "Projection is not redaction; selecting sensitive paths can print Secrets or live OTP values. Use the command secret sink where supported and keep output private."
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"secretFile": "./private/webhook-secret.txt"
},
"message": "Copy this secret now — it will not be shown again until you regenerate"
}
}
]
{
"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
}
}
pipelines webhook-secret regenerate
Rotate a compromised or lost webhook signing secret, invalidating the previous secret immediately.
When to use
- Use only when ready to deploy the replacement to the webhook receiver.
How it works
- Rotates the current secret, invalidating the previous value. With
--secret-file, the CLI removes the secret field from JSON and writes it privately; without it, JSON contains the secret. Acknowledge after the replacement has been safely captured.
Prerequisites
- A CLI login with permission for this account and resource.
- For secret-safe machine output, choose a new private
--secret-filepath; omitting it places webhookSigningSecret in the JSON response.
Expected outcome
A newly rotated webhook signing secret. A private file sink returns secretFile without the secret; without --secret-file the secret is present in JSON.
Next steps
- Deploy the replacement secret, then acknowledge it; until deployment, signed webhook verification using the old secret will fail.
Usage
akedly pipelines webhook-secret regenerate <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines webhook-secret regenerate" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--secret-file | text | — | Write the secret to a new mode 0600 file and omit it from output; an existing path is refused. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--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: pipelines:update.
Server request: POST /pipelines/id/:id/webhook-secret/regenerate.
Consent
--yes. --dry-run bypasses confirmation where supported. Destructive: true. Spends money: false. Real send: false.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
Output
Sensitive output: secret-sink. Use the supported sink; default output may contain a secret. Supported sinks: --secret-file.
Full command details
Rotate the webhook signing secret; the old secret stops immediately. Save the new secret before acknowledging it. The new secret is printed by default; prefer --secret-file <new path> to save it privately instead.
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.
Synthetic pipelines webhook-secret regenerate workflow
Rotate a compromised or lost webhook signing secret, invalidating the previous secret immediately.
akedly pipelines webhook-secret regenerate 0123456789abcdef01234567 --yes --secret-file ./private/webhook-secret.txt
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"secretFile": "./private/webhook-secret.txt"
},
"message": "Webhook signing secret regenerated successfully. Update your server immediately."
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Secret output sink: ./private/webhook-secret.txt. Use a new private path when running the command; the synthetic example is documentation only.
Additional command forms
akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path>
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "secret-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "output-sink",
"contract": "secret-output",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"fileContract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"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": "secret-file",
"inputRole": "output-sink",
"transform": "identity",
"fileContract": "secret-output",
"contributors": [
"secret-file"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [
{
"name": "secret-output",
"contract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"uses": [
{
"flag": "secret-file",
"role": "output-sink",
"purpose": null
}
]
}
],
"wire": {
"method": "POST",
"route": "/pipelines/id/:id/webhook-secret/regenerate",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
Without --secret-file, the CLI passes through the webhookSigningSecret in JSON. Use a private --secret-file to keep secret bytes out of stdout.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"webhookSigningSecret": {
"type": "string",
"pattern": "^whsec_[A-Za-z0-9+/]{43}=$"
},
"secretFile": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--secret-file receives one-time secret delivery",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"secretFile": {
"type": "string"
}
},
"required": [
"secretFile"
],
"not": {
"required": [
"webhookSigningSecret"
]
},
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"With --secret-file, secretDelivery removes data.webhookSigningSecret and adds data.secretFile. The secret bytes are written to the selected file."
],
"title": "--secret-file receives one-time secret delivery"
},
{
"when": "--secret-file is omitted",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"webhookSigningSecret": {
"type": "string",
"pattern": "^whsec_[A-Za-z0-9+/]{43}=$"
},
"secretFile": {
"type": "string"
}
},
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"The CLI passes through data.webhookSigningSecret in JSON output. Treat stdout as secret material; do not log or expose it."
],
"title": "--secret-file is omitted"
},
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1/pipelines/id/0123456789abcdef01234567/webhook-secret/regenerate",
"body": {}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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": "Projection is not redaction; selecting sensitive paths can print Secrets or live OTP values. Use the command secret sink where supported and keep output private."
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"secretFile": "./private/webhook-secret.txt"
},
"message": "Webhook signing secret regenerated successfully. Update your server immediately."
}
}
]
{
"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
}
}
pipelines webhook-secret acknowledge
Confirm that the replacement webhook secret has been securely captured, allowing the secret lifecycle to be closed.
When to use
- Use after saving a newly issued or revealed secret; supply either the saved secret file or its exact final four characters.
How it works
- Sends only the secret suffix for server-side acknowledgement; confirmation required.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Acknowledgement result without returning secret material.
Next steps
- Keep the saved secret in the webhook receiver’s secret store.
Usage
akedly pipelines webhook-secret acknowledge <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines webhook-secret acknowledge" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--secret-file | text | — | Command-specific option; see the full command details below. Manifest body field: secretSuffix. |
--secret-suffix | text | — | Command-specific option; see the full command details below. Manifest body field: secretSuffix. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--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: pipelines:update.
Server request: POST /pipelines/id/:id/webhook-secret/acknowledge.
Consent
--yes. --dry-run bypasses confirmation where supported. Destructive: true. 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
Acknowledge holding the secret using exactly one file or four-character suffix. After acknowledgement reveal is refused.
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.
Synthetic pipelines webhook-secret acknowledge workflow
Confirm that the replacement webhook secret has been securely captured, allowing the secret lifecycle to be closed.
akedly pipelines webhook-secret acknowledge 0123456789abcdef01234567 --secret-suffix AQE= --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"message": "Secret acknowledged"
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Synthetic acknowledge from the saved webhook-secret file
Confirm that the replacement webhook signing secret was saved so its rotation can be acknowledged.
akedly pipelines webhook-secret acknowledge 0123456789abcdef01234567 --secret-file ./fixtures/saved-webhook-secret.txt --yes
Input file: ./fixtures/saved-webhook-secret.txt (text).
[REDACTED — use your privately saved secret file]
Illustrative response (sensitive values omitted):
{
"status": "success",
"message": "Secret acknowledged"
}
Prerequisites: an authorized CLI login and the exact saved secret file for this pipeline. The CLI sends only the file's final four characters for acknowledgement; it does not return a data object.
Secret fixture content is synthetic and must be redacted from any rendered human guide. Keep the real saved secret in the webhook receiver's secret store. Acknowledgement confirms capture; it does not revoke or consume the saved value.
Additional command forms
akedly pipelines webhook-secret acknowledge <id> --secret-file <saved path> --yes
akedly pipelines webhook-secret acknowledge <id> --secret-suffix <last 4 characters> --yes
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "secret-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "input-file",
"contract": "secret-input",
"requestTargets": [
{
"role": "body",
"path": "secretSuffix",
"transform": "secret-suffix"
}
],
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Pipeline webhook signing secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^[\\s]*\\S[^\\r\\n]*[\\s]*$",
"description": "Single-line secret text; surrounding whitespace is trimmed before the CLI sends only its suffix."
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"minLength": 1,
"pattern": "^\\s*\\S[^\\r\\n]*\\s*$"
},
"fileContract": {
"format": "single-line-secret",
"maxBytes": 4096,
"regularFile": true,
"trim": true,
"reader": "secrets.js:readSecretFile",
"type": "single-line-secret",
"exclusive": false,
"mode": null,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Pipeline webhook signing secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^[\\s]*\\S[^\\r\\n]*[\\s]*$",
"description": "Single-line secret text; surrounding whitespace is trimmed before the CLI sends only its suffix."
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "secret-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "secret-input",
"contributors": [
"secret-file"
]
},
{
"role": "body",
"path": "secretSuffix",
"transform": "secret-suffix",
"input": "secret-file",
"contributors": [
"secret-file"
],
"fileContract": "secret-input"
}
]
},
{
"name": "secret-suffix",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "secretSuffix",
"transform": "identity"
}
],
"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": "body",
"path": "secretSuffix",
"transform": "identity",
"input": "secret-suffix",
"contributors": [
"secret-suffix"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [
{
"name": "secret-input",
"contract": {
"format": "single-line-secret",
"maxBytes": 4096,
"regularFile": true,
"trim": true,
"reader": "secrets.js:readSecretFile",
"type": "single-line-secret",
"exclusive": false,
"mode": null,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"minLength": 1,
"pattern": "^\\s*\\S[^\\r\\n]*\\s*$"
},
"uses": [
{
"flag": "secret-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Pipeline webhook signing secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^[\\s]*\\S[^\\r\\n]*[\\s]*$",
"description": "Single-line secret text; surrounding whitespace is trimmed before the CLI sends only its suffix."
}
}
]
}
],
"wire": {
"method": "POST",
"route": "/pipelines/id/:id/webhook-secret/acknowledge",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The success response contains status and message only; it has no data field.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"message": {
"type": "string"
}
},
"required": [
"status",
"message"
],
"additionalProperties": true
},
"variants": [
{
"when": "Already acknowledged",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"message": {
"type": "string"
}
},
"required": [
"status",
"message"
],
"additionalProperties": true
},
"example": {
"status": "success",
"message": "Already acknowledged"
},
"notes": [
"The response has status and message only; no data field."
],
"title": "Already acknowledged"
},
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1/pipelines/id/0123456789abcdef01234567/webhook-secret/acknowledge",
"body": {
"secretSuffix": "AQE="
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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 successful response",
"value": {
"status": "success",
"message": "Secret acknowledged"
}
}
]
{
"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
}
}
pipelines webhook-test preview
Inspect the payload and signing headers Akedly would use for a selected test webhook event.
When to use
- Use to inspect the supported event list, generated signature-header placeholders and synthetic payload before testing a receiver.
How it works
- Builds a preview from the owned pipeline and requested event; read-only.
Prerequisites
- An existing owned pipeline with an allowed test event for its API version and product; a configured callback URL is needed for the later run, not the preview.
Expected outcome
Preview payload and headers returned by the API.
Next steps
- Check that the receiver accepts the event shape before using webhook-test run.
Usage
akedly pipelines webhook-test preview <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines webhook-test preview" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--event | text | — | Command-specific option; see the full command details below. Query parameter: event. |
--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: pipelines:read.
Server request: GET /pipelines/id/:id/webhook-test/preview.
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
Preview the sample webhook payload and placeholder signing headers.
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.
Synthetic pipelines webhook-test preview workflow
Inspect the payload and signing headers Akedly would use for a selected test webhook event.
akedly pipelines webhook-test preview 0123456789abcdef01234567 --event verification
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"event": "verification",
"events": [
"verification"
],
"method": "POST",
"url": "https://receiver.example.invalid/hooks/akedly",
"headers": {
"Content-Type": "application/json",
"User-Agent": "Akedly-Webhook/1.0",
"svix-id": "msg_•••••• (generated)",
"svix-timestamp": "•••••••••• (generated)",
"svix-signature": "v1,•••••• (generated)"
},
"body": {
"event": "webhook.test",
"test": true,
"mainTransaction": {
"transactionID": "tx_example_001",
"status": "Successful",
"verificationAddress": {
"phoneNumber": "+201234567890"
},
"OTP": "123456",
"resolvedChannel": "whatsapp",
"channels": [
"whatsapp"
],
"isTest": false,
"userID": "0123456789abcdef01234567",
"pipelineID": "89abcdef0123456789abcdef",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"updateDate": "2030-03-12T09:00:00.000Z",
"verificationMethod": "Whatsapp"
},
"transactionReq": {
"_id": "fedcba987654321001234567",
"mainTransactionID": "tx_example_001",
"status": "Successful",
"isTest": false,
"channels": [
"whatsapp"
],
"sentVerificationDate": "2030-03-12T08:57:00.000Z",
"inputOTP": "123456",
"verificationDate": "2030-03-12T09:00:00.000Z",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"verificationMethod": "Whatsapp"
}
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines webhook-test preview <id>
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "event",
"transform": "identity",
"role": "query",
"input": "event",
"contributors": [
"event"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/pipelines/id/:id/webhook-test/preview",
"query": {
"event": [
{
"path": "event",
"transform": "identity",
"role": "query",
"input": "event",
"contributors": [
"event"
]
}
]
},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
The sample payload contains synthetic transaction and OTP values; actual previews are generated from a synthetic test transaction and vary by pipeline version/product.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"event": {
"type": "string"
},
"payload": {
"type": "object"
},
"headers": {
"type": "object"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"event": "verification",
"events": [
"verification"
],
"method": "POST",
"url": "https://receiver.example.invalid/hooks/akedly",
"headers": {
"Content-Type": "application/json",
"User-Agent": "Akedly-Webhook/1.0",
"svix-id": "msg_•••••• (generated)",
"svix-timestamp": "•••••••••• (generated)",
"svix-signature": "v1,•••••• (generated)"
},
"body": {
"event": "webhook.test",
"test": true,
"mainTransaction": {
"transactionID": "tx_example_001",
"status": "Successful",
"verificationAddress": {
"phoneNumber": "+201234567890"
},
"OTP": "123456",
"resolvedChannel": "whatsapp",
"channels": [
"whatsapp"
],
"isTest": false,
"userID": "0123456789abcdef01234567",
"pipelineID": "89abcdef0123456789abcdef",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"updateDate": "2030-03-12T09:00:00.000Z",
"verificationMethod": "Whatsapp"
},
"transactionReq": {
"_id": "fedcba987654321001234567",
"mainTransactionID": "tx_example_001",
"status": "Successful",
"isTest": false,
"channels": [
"whatsapp"
],
"sentVerificationDate": "2030-03-12T08:57:00.000Z",
"inputOTP": "123456",
"verificationDate": "2030-03-12T09:00:00.000Z",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"verificationMethod": "Whatsapp"
}
}
}
}
}
]
{
"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
}
}
pipelines webhook-test run
Send a signed test event to the pipeline’s configured webhook URL to verify receiver connectivity.
When to use
- Use after previewing the event and confirming the configured URL is a safe test receiver.
How it works
- Requires explicit confirmation; this makes an outbound webhook request and can trigger receiver-side effects.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Delivery attempt result returned by the server.
Next steps
- Inspect receiver logs and delivery result; avoid automatic retries if the outcome is ambiguous.
Usage
akedly pipelines webhook-test run <id>
Inspect this command offline before preparing an operation:
akedly describe "pipelines webhook-test run" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--event | text | — | Command-specific option; see the full command details below. Manifest body field: event. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--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: pipelines:update.
Server request: POST /pipelines/id/:id/webhook-test.
Consent
--yes. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: true.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
Output
Sensitive output: echo-scrub. echo-scrub masks secret-shaped text, credential fields, secret query parameters and JSON secret fields inside strings in the response, such as a webhook receiver echo or BYO-SMS steps; keep the output private anyway.
Full command details
Send a signed sample to the configured backend callback URL. Exit 0 means the request ran; read the top-level delivered (false on delivery failure), statusCode, error and tip. Secret-shaped text in any response string is shown as [REDACTED].
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.
Synthetic pipelines webhook-test run workflow
Send a signed test event to the pipeline’s configured webhook URL to verify receiver connectivity.
akedly pipelines webhook-test run 0123456789abcdef01234567 --event verification --yes
Illustrative response (sensitive values omitted):
{
"delivered": true,
"statusCode": 200,
"durationMs": 12,
"error": null,
"tip": null,
"request": {
"method": "POST",
"url": "https://receiver.example.invalid/hooks/akedly",
"headers": {
"Content-Type": "application/json",
"User-Agent": "Akedly-Webhook/1.0",
"svix-id": "msg_fixture_001",
"svix-timestamp": "1890000000",
"svix-signature": "v1,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
},
"body": {
"event": "webhook.test",
"test": true,
"mainTransaction": {
"transactionID": "tx_example_001",
"status": "Successful",
"verificationAddress": {
"phoneNumber": "+201234567890"
},
"OTP": "123456",
"resolvedChannel": "whatsapp",
"channels": [
"whatsapp"
],
"isTest": false,
"userID": "0123456789abcdef01234567",
"pipelineID": "89abcdef0123456789abcdef",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"updateDate": "2030-03-12T09:00:00.000Z",
"verificationMethod": "Whatsapp"
},
"transactionReq": {
"_id": "fedcba987654321001234567",
"mainTransactionID": "tx_example_001",
"status": "Successful",
"isTest": false,
"channels": [
"whatsapp"
],
"sentVerificationDate": "2030-03-12T08:57:00.000Z",
"inputOTP": "123456",
"verificationDate": "2030-03-12T09:00:00.000Z",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"verificationMethod": "Whatsapp"
}
}
},
"response": {
"body": "accepted"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
This operation contacts the configured webhook receiver; the synthetic example is not to be executed.
The receiver URL must be configured on the existing pipeline. This command contacts it; the sample must not be executed.
Additional command forms
akedly pipelines webhook-test run <id> --yes
akedly pipelines webhook-test run <id> --yes --fields delivered,statusCode,error,tip
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "event",
"transform": "identity",
"role": "body",
"input": "event",
"contributors": [
"event"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "POST",
"route": "/pipelines/id/:id/webhook-test",
"query": {},
"body": {
"event": [
{
"path": "event",
"transform": "identity",
"role": "body",
"input": "event",
"contributors": [
"event"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
Delivery failures are HTTP 200 command results with delivered:false; statusCode and response.body can be null. Inspect error and tip before deciding whether to retry.
The request snapshot includes the synthetic test payload and signed headers. Actual timestamps, IDs and signatures are generated per run.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"delivered": {
"type": "boolean"
},
"statusCode": {
"type": [
"integer",
"null"
]
},
"durationMs": {
"type": "integer"
},
"error": {
"type": [
"string",
"null"
]
},
"tip": {
"type": [
"string",
"null"
]
},
"request": {
"type": "object",
"properties": {
"method": {
"const": "POST"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url",
"headers",
"body"
],
"additionalProperties": true
},
"response": {
"type": "object",
"properties": {
"body": {
"type": [
"string",
"null"
]
}
},
"required": [
"body"
],
"additionalProperties": true
}
},
"required": [
"delivered",
"statusCode",
"durationMs",
"error",
"tip",
"request",
"response"
],
"additionalProperties": true
},
"variants": [
{
"when": "Receiver is unreachable or times out",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"delivered": {
"type": "boolean"
},
"statusCode": {
"type": [
"integer",
"null"
]
},
"durationMs": {
"type": "integer"
},
"error": {
"type": [
"string",
"null"
]
},
"tip": {
"type": [
"string",
"null"
]
},
"request": {
"type": "object",
"properties": {
"method": {
"const": "POST"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url",
"headers",
"body"
],
"additionalProperties": true
},
"response": {
"type": "object",
"properties": {
"body": {
"type": [
"string",
"null"
]
}
},
"required": [
"body"
],
"additionalProperties": true
}
},
"required": [
"delivered",
"statusCode",
"durationMs",
"error",
"tip",
"request",
"response"
],
"additionalProperties": true
},
"notes": [
"The endpoint responds HTTP 200 with delivered:false; statusCode and response.body may be null."
],
"example": {
"delivered": false,
"statusCode": null,
"durationMs": 12,
"error": "unreachable",
"tip": "Could not reach your URL. Make sure it is publicly accessible and not behind a firewall or localhost.",
"request": {
"method": "POST",
"url": "https://receiver.example.invalid/hooks/akedly",
"headers": {
"Content-Type": "application/json",
"User-Agent": "Akedly-Webhook/1.0",
"svix-id": "msg_fixture_001",
"svix-timestamp": "1890000000",
"svix-signature": "v1,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
},
"body": {
"event": "webhook.test",
"test": true,
"mainTransaction": {
"transactionID": "tx_example_001",
"status": "Successful",
"verificationAddress": {
"phoneNumber": "+201234567890"
},
"OTP": "123456",
"resolvedChannel": "whatsapp",
"channels": [
"whatsapp"
],
"isTest": false,
"userID": "0123456789abcdef01234567",
"pipelineID": "89abcdef0123456789abcdef",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"updateDate": "2030-03-12T09:00:00.000Z",
"verificationMethod": "Whatsapp"
},
"transactionReq": {
"_id": "fedcba987654321001234567",
"mainTransactionID": "tx_example_001",
"status": "Successful",
"isTest": false,
"channels": [
"whatsapp"
],
"sentVerificationDate": "2030-03-12T08:57:00.000Z",
"inputOTP": "123456",
"verificationDate": "2030-03-12T09:00:00.000Z",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"verificationMethod": "Whatsapp"
}
}
},
"response": {
"body": null
}
},
"title": "Receiver is unreachable or times out"
},
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1/pipelines/id/0123456789abcdef01234567/webhook-test",
"body": {
"event": "verification"
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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": "The CLI scrubs recognized secret values (prefixed secrets, credential fields, secret query parameters and JSON secret fields inside strings) from the response before projection; keep the output private anyway."
}
}
[
{
"title": "Synthetic successful response",
"value": {
"delivered": true,
"statusCode": 200,
"durationMs": 12,
"error": null,
"tip": null,
"request": {
"method": "POST",
"url": "https://receiver.example.invalid/hooks/akedly",
"headers": {
"Content-Type": "application/json",
"User-Agent": "Akedly-Webhook/1.0",
"svix-id": "msg_fixture_001",
"svix-timestamp": "1890000000",
"svix-signature": "v1,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
},
"body": {
"event": "webhook.test",
"test": true,
"mainTransaction": {
"transactionID": "tx_example_001",
"status": "Successful",
"verificationAddress": {
"phoneNumber": "+201234567890"
},
"OTP": "123456",
"resolvedChannel": "whatsapp",
"channels": [
"whatsapp"
],
"isTest": false,
"userID": "0123456789abcdef01234567",
"pipelineID": "89abcdef0123456789abcdef",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"updateDate": "2030-03-12T09:00:00.000Z",
"verificationMethod": "Whatsapp"
},
"transactionReq": {
"_id": "fedcba987654321001234567",
"mainTransactionID": "tx_example_001",
"status": "Successful",
"isTest": false,
"channels": [
"whatsapp"
],
"sentVerificationDate": "2030-03-12T08:57:00.000Z",
"inputOTP": "123456",
"verificationDate": "2030-03-12T09:00:00.000Z",
"creationDate": "2030-03-12T08:57:00.000Z",
"expirationDate": "2030-03-12T09:01:00.000Z",
"verificationMethod": "Whatsapp"
}
}
},
"response": {
"body": "accepted"
}
}
}
]
{
"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
}
}
pipelines test-challenge
Fetch a fresh challenge for the dashboard test-send flow without sending an OTP.
When to use
- Use immediately before a dashboard test send that requires PoW or Turnstile challenge data.
How it works
- Reads a fresh V1.2 challenge for the chosen pipeline.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Challenge token, difficulty, expiry, algorithm and Turnstile requirement as returned by the API.
Next steps
- Pass the fresh challenge through the test-send flow; do not reuse expired challenge data.
Usage
akedly pipelines test-challenge
Inspect this command offline before preparing an operation:
akedly describe "pipelines test-challenge" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--pipeline-id | id | — | yes | Command-specific option; see the full command details below. Query parameter: pipelineID. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: sandbox:execute.
Server request: GET /api/v1.2/transactions/dashboard/challenge.
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
Fetch a fresh V1.2 dashboard PoW challenge. Requires a Full access CLI Login with sandbox:execute and a usable account API key; Read-only logins and logins approved by Akedly staff without full access are refused. First enable dev mode explicitly with pipelines dev-mode set <id> --dev-mode on --yes and use the returned fake Test Pair, valid for about an hour. Test commands never enable dev mode or bypass flags.
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.
Synthetic pipelines test-challenge workflow
Fetch a fresh challenge for the dashboard test-send flow without sending an OTP.
akedly pipelines test-challenge --pipeline-id 0123456789abcdef01234567
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"challengeRequired": true,
"challengeToken": "synthetic-token",
"challenge": "synthetic-challenge",
"difficulty": 2,
"expiresAt": "2030-03-12T09:05:00.000Z",
"algorithm": "sha256",
"turnstile": {
"required": false,
"siteKey": null
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines test-challenge --pipeline-id <id>
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": "pipeline-id",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}$"
},
"required": true,
"wire": [
{
"path": "pipelineID",
"transform": "identity",
"role": "query",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"validation": {
"syntax": "24-digit hexadecimal object ID."
}
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/api/v1.2/transactions/dashboard/challenge",
"query": {
"pipeline-id": [
{
"path": "pipelineID",
"transform": "identity",
"role": "query",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
]
},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"challengeRequired": {
"type": "boolean"
},
"challengeToken": {
"type": "string"
},
"challenge": {
"type": "string"
},
"difficulty": {
"type": "integer"
},
"expiresAt": {
"type": "string"
},
"algorithm": {
"type": "string"
},
"turnstile": {
"type": "object"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"challengeRequired": true,
"challengeToken": "synthetic-token",
"challenge": "synthetic-challenge",
"difficulty": 2,
"expiresAt": "2030-03-12T09:05:00.000Z",
"algorithm": "sha256",
"turnstile": {
"required": false,
"siteKey": null
}
}
}
}
]
{
"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
}
}
pipelines test-send
Send a dashboard test OTP to validate the pipeline and recipient path.
When to use
- Use only with an authorized test destination and explicit confirmation; a real SMS/WhatsApp delivery may incur provider cost.
How it works
- Fetches a fresh challenge, solves PoW where required, then sends once; ambiguous POST outcomes are not automatically retried.
Prerequisites
- An existing V1.2 OTP pipeline owned by the logged-in account.
- An authorized test destination; this command sends an OTP and may incur provider cost.
Expected outcome
Transaction IDs for the send request.
Next steps
- Check delivery and use test-verify with the transaction request ID if verifying the code.
Usage
akedly pipelines test-send
Inspect this command offline before preparing an operation:
akedly describe "pipelines test-send" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--pipeline-id | id | — | yes | Command-specific option; see the full command details below. Manifest body field: pipelineID. |
--phone-number | text | — | yes | Command-specific option; see the full command details below. Manifest body field: verificationAddress.phoneNumber. |
--turnstile-token | text | — | no | Command-specific option; see the full command details below. Manifest body field: turnstileToken. |
--yes | boolean switch | — | no | Confirm this action where consent is required. |
--dry-run | boolean switch | — | no | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: sandbox:execute.
Server request: POST /api/v1.2/transactions/dashboard/send.
Consent
--yes. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: true.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
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
Fetch and solve a fresh PoW challenge, then send once. Requires --yes and a Full access CLI Login with sandbox:execute and a usable account API key; Read-only logins and logins approved by Akedly staff without full access are refused. The send is real and billed unless the number is the pipeline's dev-mode Test Pair. For a free test, first run akedly pipelines dev-mode set <id> --dev-mode on --yes and send to the Test Pair it returns (valid for about an hour). This command never enables dev mode or bypass flags. An ambiguous failure may have sent a message; check the destination before retrying. Dry runs send nothing and need no CLI Login.
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.
Synthetic pipelines test-send workflow
Send a dashboard test OTP to validate the pipeline and recipient path.
akedly pipelines test-send --pipeline-id 0123456789abcdef01234567 --phone-number +201234567890 --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"transactionID": "tx_example_001",
"transactionReqID": "89abcdef0123456789abcdef"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
This operation sends an OTP and may incur cost; the synthetic example is not to be executed.
Additional command forms
akedly pipelines test-send --pipeline-id <id> --phone-number <number> --yes
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": "pipeline-id",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}$"
},
"required": true,
"wire": [
{
"path": "pipelineID",
"transform": "identity",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"validation": {
"syntax": "24-digit hexadecimal object ID."
}
},
{
"name": "phone-number",
"schema": {
"type": "string"
},
"required": true,
"wire": [
{
"path": "verificationAddress.phoneNumber",
"transform": "identity",
"role": "body",
"input": "phone-number",
"contributors": [
"phone-number"
]
}
]
},
{
"name": "turnstile-token",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "turnstileToken",
"transform": "identity",
"role": "body",
"input": "turnstile-token",
"contributors": [
"turnstile-token"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "POST",
"route": "/api/v1.2/transactions/dashboard/send",
"query": {},
"body": {
"pipeline-id": [
{
"path": "pipelineID",
"transform": "identity",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"phone-number": [
{
"path": "verificationAddress.phoneNumber",
"transform": "identity",
"role": "body",
"input": "phone-number",
"contributors": [
"phone-number"
]
}
],
"turnstile-token": [
{
"path": "turnstileToken",
"transform": "identity",
"role": "body",
"input": "turnstile-token",
"contributors": [
"turnstile-token"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"transactionID": {
"type": "string"
},
"transactionReqID": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1.2/transactions/dashboard/send",
"body": {
"pipelineID": "0123456789abcdef01234567",
"verificationAddress": {
"phoneNumber": "+201234567890"
},
"powSolution": {
"challengeToken": "<fresh challenge and solution>"
}
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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 successful response",
"value": {
"status": "success",
"data": {
"transactionID": "tx_example_001",
"transactionReqID": "89abcdef0123456789abcdef"
}
}
}
]
{
"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
}
}
pipelines test-verify
Complete a V1.2 dashboard test verification by checking the OTP against its transaction request.
When to use
- Use only after a V1.2 dashboard test-send has returned a transactionReqID and the test code is available.
How it works
- Checks transaction ownership for the logged-in account, then calls the V1.2 secure OTP verifier. A successful response returns verified, transactionID and frontendCallbackURL, and may include enrollmentToken for eligible phone verification.
Prerequisites
- A transactionReqID returned by a V1.2 dashboard test-send for this account, plus the received test OTP.
Expected outcome
Transaction request status returned by the API.
Next steps
- Check the final status; if expired or failed, begin a new test-send.
Usage
akedly pipelines test-verify <transactionReqID>
Inspect this command offline before preparing an operation:
akedly describe "pipelines test-verify" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <transactionReqID> | ^[a-fA-F0-9]{24}$ | transactionReqID must be a 24-hex transaction request id. |
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--otp | text | — | yes | Command-specific option; see the full command details below. Manifest body field: otp. |
--dry-run | boolean switch | — | no | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: sandbox:execute.
Server request: POST /api/v1.2/transactions/dashboard/verify/:transactionReqID.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
Output
Sensitive output: live-otp. Responses may contain live OTP fields; keep them private.
Full command details
Verify a dashboard V1.2 transaction using a Full access CLI Login with sandbox:execute; Read-only logins and logins approved by Akedly staff without full access are refused. Successful verification can fire the configured backend webhook; responses can include live OTP fields, so keep output private.
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.
Synthetic pipelines test-verify workflow
Verify an OTP from a dashboard test-send transaction.
akedly pipelines test-verify 89abcdef0123456789abcdef --otp 123456
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"verified": true,
"transactionID": "tx_example_001",
"frontendCallbackURL": "https://app.example.invalid/verified"
},
"message": "OTP verified successfully"
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines test-verify <transactionReqID> --otp 1234
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": [
{
"name": "transactionReqID",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"validation": "transactionReqID must be a 24-hex transaction request id.",
"mapsToRouteParameter": "transactionReqID",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "otp",
"schema": {
"type": "string"
},
"required": true,
"wire": [
{
"path": "otp",
"transform": "identity",
"role": "body",
"input": "otp",
"contributors": [
"otp"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "POST",
"route": "/api/v1.2/transactions/dashboard/verify/:transactionReqID",
"query": {},
"body": {
"otp": [
{
"path": "otp",
"transform": "identity",
"role": "body",
"input": "otp",
"contributors": [
"otp"
]
}
]
},
"ownerParams": []
}
}
Response contract
This command uses the V1.2 dashboard verification endpoint. The success DTO contains verified, transactionID and frontendCallbackURL; enrollmentToken may be present for eligible phone verifications.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"verified": {
"type": "boolean"
},
"transactionID": {
"type": "string"
},
"frontendCallbackURL": {
"type": "string"
},
"enrollmentToken": {
"type": "string"
}
},
"required": [
"verified",
"transactionID",
"frontendCallbackURL"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1.2/transactions/dashboard/verify/89abcdef0123456789abcdef",
"body": {
"otp": "123456"
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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": "Projection is not redaction; selecting sensitive paths can print Secrets or live OTP values. Use the command secret sink where supported and keep output private."
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"verified": true,
"transactionID": "tx_example_001",
"frontendCallbackURL": "https://app.example.invalid/verified"
},
"message": "OTP verified successfully"
}
}
]
{
"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
}
}
pipelines sandbox-demo ensure
Provision or retrieve the account’s isolated sandbox demo pipeline and associated demo resources.
When to use
- Use for a sandbox walkthrough when a demo setup is needed; it is not a production pipeline.
How it works
- Ensures demo resources for the requested API version; repeated calls converge on the sandbox setup.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
The ensured demo version, a safe pipeline projection, and either widget:null (V1.2) or an ensured widget projection (V2).
Next steps
- For V1.2, use the direct API demo flow. For V2, inspect the returned widget, then finish hosted-widget setup as required; provisioning does not by itself complete a production embed.
Usage
akedly pipelines sandbox-demo ensure
Inspect this command offline before preparing an operation:
akedly describe "pipelines sandbox-demo ensure" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--api-version | enum | v1.2 | v2 | yes | Command-specific option; see the full command details below. Manifest body field: version. Tip: Use V2 if you will embed Akedly's hosted widget, and V1.2 if you will build the verification screens yourself or cannot embed an iframe or open a system browser. V1.0 is legacy and sunsets on December 30, 2026. |
--yes | boolean switch | — | no | Confirm this action where consent is required. |
--dry-run | boolean switch | — | no | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: sandbox:execute.
Server request: POST /pipelines/sandbox-demo/ensure.
Consent
--yes. --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
Ensure the signed-in account's OTP sandbox demo resources with --api-version v1.2|v2 --yes (--version prints the CLI package version). Requires a Full access CLI Login with sandbox:execute and pipelines:create; V2 also requires widgets:create. Read-only logins are refused. This can adopt your existing unmarked OTP pipeline (or legacy pipeline with no product) named exactly "Sandbox Demo Pipeline" (for v1.2) or "Sandbox Widget Pipeline" (for v2) and the same API version and, for V2, an unmarked widget attached to it (one named "Sandbox Widget" first), or reattach a demo widget whose pipeline was deleted. Each run resets the demo pipeline's API version and, for V2, both callback URLs, so callback changes made with pipelines update do not persist. These writes need only the permissions above, not pipelines:update or widgets:update; --yes confirms them. New demo pipelines are active Test pipelines. V2 callbacks point to the environment's dashboard /admin and API /sandbox-webhook-sink. The API does not serve /sandbox-webhook-sink yet, so this callback is not a working demo webhook receiver; test your own handler with akedly pipelines webhook-test run <id> --yes on your own pipeline instead. This is separate from akedly utilities sandbox status/send and prepares OTP resources without sending, charging or enabling dev mode. Next for v1.2: run akedly pipelines dev-mode set <id> --dev-mode on --yes (needs pipelines:dev_mode), then akedly pipelines test-send --pipeline-id <id> --phone-number <Test Pair number> --yes, then akedly pipelines test-verify <transactionReqID> --otp <Test Pair OTP>, using data.transactionReqID from test-send. Use the current returned data.devMode.testPair phoneNumber and otp from dev-mode set while that window is active; only this Test Pair is free. test-send needs sandbox:execute and a usable server-selected account API key; any other number is a real, billed send. Inspect the returned pipeline id, name, API version, channel and server-reported devMode enabled/expiresAt; an enabled window can already be expired. V2 also returns widget id/publicKey, without its Secret. Reused or adopted pipelines and widgets keep their current active state and status, which the response does not show (reused widgets are not reactivated); check them with akedly pipelines get <id> and akedly widgets get <id>; reactivate with akedly pipelines update <id> --status active --yes or akedly widgets update <id> --status active --yes. No Test Pair, callback URLs, secrets or authentication URL are returned. This prepared V2 demo is not complete headless V2 setup: open the dashboard Home or Authentication page and choose Test now → Drop-in widget. Its sandbox launch requires an allowed dashboard Origin and session; the widget Secret is not returned here. Set widget dev mode with akedly widgets update <widgetId> --dev-mode on --yes (needs widgets:update), and read its current Test Pair at data.devMode.testPair with akedly widgets get <widgetId>. Resources can remain partly prepared after failure: repeat this command with the same --api-version to converge, except after a SANDBOX_PIPELINE_PURPOSE_CONFLICT or SANDBOX_WIDGET_PURPOSE_CONFLICT, which repeating will not fix; do not automatically delete them. Run akedly pipelines list first to inspect possible same-name adoption; dry-run does not inspect existing resources. Dry-run validates and previews offline without consent, credentials or requests.
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.
Synthetic pipelines sandbox-demo ensure workflow
Provision or retrieve the account’s isolated sandbox demo pipeline and associated demo resources.
akedly pipelines sandbox-demo ensure --api-version v1.2 --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"version": "v1.2",
"pipeline": {
"_id": "0123456789abcdef01234567",
"name": "Sandbox Demo Pipeline",
"apiVersion": "v1.2",
"channel": "whatsapp",
"devMode": {
"enabled": false
}
},
"widget": null
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly pipelines sandbox-demo ensure --api-version v1.2 --yes
akedly pipelines sandbox-demo ensure --api-version v2 --yes
akedly pipelines sandbox-demo ensure --api-version v2 --dry-run
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": "api-version",
"schema": {
"type": "string",
"enum": [
"v1.2",
"v2"
]
},
"required": true,
"requiresConsent": true,
"wire": [
{
"path": "version",
"transform": "identity",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "POST",
"route": "/pipelines/sandbox-demo/ensure",
"query": {},
"body": {
"api-version": [
{
"path": "version",
"transform": "identity",
"role": "body",
"input": "api-version",
"contributors": [
"api-version"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"version": {
"type": "string"
},
"pipeline": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"apiVersion": {
"type": "string"
},
"channel": {
"type": "string"
},
"devMode": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"expiresAt": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
}
},
"required": [
"_id",
"name",
"apiVersion"
],
"additionalProperties": true
},
"widget": {
"type": [
"object",
"null"
]
}
},
"required": [
"version",
"pipeline",
"widget"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1/pipelines/sandbox-demo/ensure",
"body": {
"version": "v1.2"
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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 successful response",
"value": {
"status": "success",
"data": {
"version": "v1.2",
"pipeline": {
"_id": "0123456789abcdef01234567",
"name": "Sandbox Demo Pipeline",
"apiVersion": "v1.2",
"channel": "whatsapp",
"devMode": {
"enabled": false
}
},
"widget": null
}
}
}
]
{
"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": {
"INVALID_SANDBOX_VERSION": "Use --api-version v1.2 or --api-version v2; --version prints the CLI package version.",
"SANDBOX_INDEX_UNAVAILABLE": "Sandbox purpose indexes are unavailable. Retry later with the same --api-version or contact Akedly support.",
"SANDBOX_PIPELINE_PURPOSE_CONFLICT": "An incompatible pipeline holds the demo purpose. Inspect akedly pipelines list and ask Akedly support to resolve the conflict before repeating ensure.",
"SANDBOX_WIDGET_PURPOSE_CONFLICT": "The demo widget purpose is attached to another pipeline. Inspect akedly widgets list and akedly widgets list --status inactive, and ask Akedly support to resolve the conflict; do not delete resources automatically.",
"SANDBOX_RESOURCE_CHANGED": "A sandbox resource changed during preparation. Repeat akedly pipelines sandbox-demo ensure with the same --api-version and --yes.",
"SANDBOX_ENSURE_FAILED": "Resources may be partly prepared. Repeat akedly pipelines sandbox-demo ensure with the same --api-version and --yes; if it keeps failing, contact Akedly support."
},
"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
}
}
Own recovery guidance
| Code | Hint |
|---|---|
| INVALID_SANDBOX_VERSION | Use --api-version v1.2 or --api-version v2; --version prints the CLI package version. |
| SANDBOX_INDEX_UNAVAILABLE | Sandbox purpose indexes are unavailable. Retry later with the same --api-version or contact Akedly support. |
| SANDBOX_PIPELINE_PURPOSE_CONFLICT | An incompatible pipeline holds the demo purpose. Inspect akedly pipelines list and ask Akedly support to resolve the conflict before repeating ensure. |
| SANDBOX_WIDGET_PURPOSE_CONFLICT | The demo widget purpose is attached to another pipeline. Inspect akedly widgets list and akedly widgets list --status inactive, and ask Akedly support to resolve the conflict; do not delete resources automatically. |
| SANDBOX_RESOURCE_CHANGED | A sandbox resource changed during preparation. Repeat akedly pipelines sandbox-demo ensure with the same --api-version and --yes. |
| SANDBOX_ENSURE_FAILED | Resources may be partly prepared. Repeat akedly pipelines sandbox-demo ensure with the same --api-version and --yes; if it keeps failing, contact Akedly support. |
zero-verify-bans list
Review phone, email, or device identities currently banned for repeated verification attempts that never completed.
When to use
- Use to diagnose why a customer identity cannot start another verification.
How it works
- Reads bans belonging to the logged-in account, newest first; identity values are sensitive account data.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Array of ban records with status, rung, dates, mode and lift metadata.
Next steps
- If the ban is no longer warranted, unban its record ID; otherwise wait for expiry or review the underlying behavior.
Usage
akedly zero-verify-bans list
Inspect this command offline before preparing an operation:
akedly describe "zero-verify-bans list" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--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: zero_verify_bans:read.
Server request: GET /zero-verify-bans.
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 zero-verify bans.
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.
Synthetic zero-verify-bans list workflow
Review phone, email, or device identities currently banned for repeated verification attempts that never completed.
akedly zero-verify-bans list
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": [
{
"_id": "0123456789abcdef01234567",
"identityType": "phone",
"identityValue": "+201234567890",
"rung": 1,
"bannedAt": "2030-03-12T09:00:00.000Z",
"expiresAt": "2030-03-12T10:00:00.000Z",
"status": "active",
"mode": "enforce"
}
]
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly zero-verify-bans 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
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/zero-verify-bans",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"identityType": {
"type": "string"
},
"identityValue": {
"type": "string"
},
"rung": {
"type": "integer"
},
"bannedAt": {
"type": "string"
},
"expiresAt": {
"type": "string"
},
"status": {
"type": "string"
},
"mode": {
"type": "string"
}
},
"required": [
"_id"
],
"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 successful response",
"value": {
"status": "success",
"data": [
{
"_id": "0123456789abcdef01234567",
"identityType": "phone",
"identityValue": "+201234567890",
"rung": 1,
"bannedAt": "2030-03-12T09:00:00.000Z",
"expiresAt": "2030-03-12T10:00:00.000Z",
"status": "active",
"mode": "enforce"
}
]
}
}
]
{
"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
}
}
zero-verify-bans unban
Lift one account-owned zero-verify ban and exonerate its associated identity.
When to use
- Use after confirming the identity should regain access; lifting affects enforcement immediately.
How it works
- Requires confirmation, checks ownership/current status, records a manual lift and emits an operational notification.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Updated ban record with lifted status and timestamp.
Next steps
- Retry the customer flow after lift; the identity may be banned again if the underlying pattern recurs.
Usage
akedly zero-verify-bans unban <id>
Inspect this command offline before preparing an operation:
akedly describe "zero-verify-bans unban" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--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: zero_verify_bans:update.
Server request: PATCH /zero-verify-bans/:id/unban.
Consent
--yes. --dry-run bypasses confirmation where supported. Destructive: true. Spends money: false. Real send: false.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
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
Lift a zero-verify security ban.
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.
Synthetic zero-verify-bans unban workflow
Lift one account-owned zero-verify ban and exonerate its associated identity.
akedly zero-verify-bans unban 0123456789abcdef01234567 --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"identityType": "phone",
"identityValue": "+201234567890",
"rung": 1,
"bannedAt": "2030-03-12T09:00:00.000Z",
"expiresAt": "2030-03-12T10:00:00.000Z",
"status": "lifted",
"liftedAt": "2030-03-12T09:30:00.000Z",
"liftReason": "manual",
"mode": "enforce"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly zero-verify-bans unban <id> --yes
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": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"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": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"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": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"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": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "PATCH",
"route": "/zero-verify-bans/:id/unban",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"identityType": {
"type": "string"
},
"identityValue": {
"type": "string"
},
"rung": {
"type": "integer"
},
"bannedAt": {
"type": "string"
},
"expiresAt": {
"type": "string"
},
"status": {
"type": "string"
},
"liftedAt": {
"type": "string"
},
"liftReason": {
"type": "string"
},
"mode": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "PATCH",
"url": "https://api.example.invalid/api/v1/zero-verify-bans/0123456789abcdef01234567/unban",
"body": {}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"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 successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"identityType": "phone",
"identityValue": "+201234567890",
"rung": 1,
"bannedAt": "2030-03-12T09:00:00.000Z",
"expiresAt": "2030-03-12T10:00:00.000Z",
"status": "lifted",
"liftedAt": "2030-03-12T09:30:00.000Z",
"liftReason": "manual",
"mode": "enforce"
}
}
}
]
{
"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
}
}
webhooks verify
Verify locally that a received Akedly webhook’s exact raw body matches its Svix signature and timestamp.
When to use
- Use in a receiver or incident investigation with the original body bytes, headers and current pipeline signing secret.
How it works
- Reads bounded local files, checks timestamp tolerance, and computes HMAC locally; it sends no network request.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
A valid verdict with timestampAgeSeconds, or a coded local verification error.
Next steps
- Only parse/process the webhook after valid is true; preserve raw bytes and use the active secret.
Usage
akedly webhooks verify
Inspect this command offline before preparing an operation:
akedly describe "webhooks verify" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--secret-file | text | — | no | Command-specific option; see the full command details below. |
--headers | text | — | yes | Command-specific option; see the full command details below. |
--body | text | — | yes | Command-specific option; see the full command details below. |
--tolerance-seconds | integer | 1 to 86400 | no | Command-specific option; see the full command details below. |
--at | integer | 0 to 4102444800 | no | Command-specific option; see the full command details below. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: public. Permission: none declared.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
local: Local command; no HTTP retry.
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
Verify a webhook locally using the exact raw body and a JSON headers file. Use --secret-file or AKEDLY_WEBHOOK_SECRET (file wins). Tolerance defaults to 300 seconds inclusive; --at supplies Unix seconds for historical verification. Prints no body or secret.
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.
Synthetic webhooks verify workflow
Verify locally that a received Akedly webhook’s exact raw body matches its Svix signature and timestamp.
akedly webhooks verify --secret-file ./private/webhook-secret.txt --headers ./fixtures/headers.json --body ./fixtures/body.json --at 1890000000
Input file: ./fixtures/headers.json (json).
{
"svix-id": "msg_fixture_001",
"svix-timestamp": "1890000000",
"svix-signature": "v1,DAQq9ohld8V3MrFI74qZ+kK69edzQRDy7XNEl02DaPY="
}
Input file: ./fixtures/body.json (text).
{"event":"otp.verified","data":{"transactionID":"tx_example_001"}}
Input file: ./private/webhook-secret.txt (text).
[REDACTED — use your privately saved secret file]
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"valid": true,
"timestampAgeSeconds": 0
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
The signing key and signature are synthetic and provided only to make this offline verification fixture internally consistent; never use this key in a deployed receiver.
Additional command forms
akedly webhooks verify --secret-file <path> --headers <headers.json> --body <body-file>
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": "secret-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "input-file",
"contract": "secret-input",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Webhook signing secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^\\s*whsec_[A-Za-z0-9+/]{43}=\\s*$",
"description": "One whsec_ secret with a base64 SHA-256 digest. The CLI trims surrounding whitespace before validating it."
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"minLength": 1,
"pattern": "^\\s*\\S[^\\r\\n]*\\s*$"
},
"fileContract": {
"format": "single-line-secret",
"maxBytes": 4096,
"regularFile": true,
"trim": true,
"reader": "secrets.js:readSecretFile",
"type": "single-line-secret",
"exclusive": false,
"mode": null,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Webhook signing secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^\\s*whsec_[A-Za-z0-9+/]{43}=\\s*$",
"description": "One whsec_ secret with a base64 SHA-256 digest. The CLI trims surrounding whitespace before validating it."
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "secret-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "secret-input",
"contributors": [
"secret-file"
]
}
]
},
{
"name": "headers",
"schema": {
"type": "string"
},
"required": true,
"input": {
"role": "input-file",
"contract": "json-object",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Webhook signature headers",
"description": "JSON object of HTTP headers. A successful verification needs svix-id and svix-signature non-empty strings and svix-timestamp as a non-negative integer or digit string; names are matched case-insensitively. Additional header keys and JSON values are ignored by verification.",
"type": "object",
"additionalProperties": true,
"patternProperties": {
"^[sS][vV][iI][xX]-[iI][dD]$": {
"type": "string",
"minLength": 1
},
"^[sS][vV][iI][xX]-[tT][iI][mM][eE][sS][tT][aA][mM][pP]$": {
"oneOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 999999999999
},
{
"type": "string",
"pattern": "^\\d{1,12}$"
}
]
},
"^[sS][vV][iI][xX]-[sS][iI][gG][nN][aA][tT][uU][rR][eE]$": {
"type": "string",
"minLength": 1,
"pattern": "(?:^|\\s)v1,[A-Za-z0-9+/]{43}=(?:\\s|$)"
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"type": "object",
"additionalProperties": true
},
{
"type": "array",
"items": true
}
]
}
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readJsonObject",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Webhook signature headers",
"description": "JSON object of HTTP headers. A successful verification needs svix-id and svix-signature non-empty strings and svix-timestamp as a non-negative integer or digit string; names are matched case-insensitively. Additional header keys and JSON values are ignored by verification.",
"type": "object",
"additionalProperties": true,
"patternProperties": {
"^[sS][vV][iI][xX]-[iI][dD]$": {
"type": "string",
"minLength": 1
},
"^[sS][vV][iI][xX]-[tT][iI][mM][eE][sS][tT][aA][mM][pP]$": {
"oneOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 999999999999
},
{
"type": "string",
"pattern": "^\\d{1,12}$"
}
]
},
"^[sS][vV][iI][xX]-[sS][iI][gG][nN][aA][tT][uU][rR][eE]$": {
"type": "string",
"minLength": 1,
"pattern": "(?:^|\\s)v1,[A-Za-z0-9+/]{43}=(?:\\s|$)"
}
}
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "headers",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "json-object",
"contributors": [
"headers"
]
}
]
},
{
"name": "body",
"schema": {
"type": "string"
},
"required": true,
"input": {
"role": "input-file",
"contract": "webhook-body",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Raw webhook request body bytes",
"description": "Exact raw request-body bytes represented as base64. Verification signs these bytes unchanged; do not parse and reserialize the webhook JSON. The CLI accepts up to 1 MiB.",
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "application/octet-stream"
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"contentEncoding": "base64"
},
"fileContract": {
"format": "bytes",
"maxBytes": 1048576,
"regularFile": true,
"reader": "files.js:readBoundedFile",
"type": "bytes",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Raw webhook request body bytes",
"description": "Exact raw request-body bytes represented as base64. Verification signs these bytes unchanged; do not parse and reserialize the webhook JSON. The CLI accepts up to 1 MiB.",
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "application/octet-stream"
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "body",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "webhook-body",
"contributors": [
"body"
]
}
]
},
{
"name": "tolerance-seconds",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]|(?:1[0-9]|[2-8]\\d{1}|9[0-9])|(?:1(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[2-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|(?:1(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[2-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|(?:1(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[2-7]\\d{4}|8(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-5]\\d{3}|6(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-3]\\d{2}|400))))(?:\\.0+)?)$"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "local-command-input",
"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": "tolerance-seconds",
"inputRole": "local-control",
"transform": "identity",
"purpose": "local-command-input",
"contributors": [
"tolerance-seconds"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"maximum": 86400,
"parsedValueSchema": {
"type": "integer",
"minimum": 1,
"maximum": 86400
}
}
},
{
"name": "at",
"schema": {
"type": "string",
"pattern": "^(?:[+-]?(?:0+(?:\\.0+)?|\\.0+)|\\+?0*(?:[1-9]|(?:1[0-9]|[2-8]\\d{1}|9[0-9])|(?:1(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[2-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|(?:1(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[2-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|(?:1(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[2-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|(?:1(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[2-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|(?:1(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[2-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))|(?:1(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))|[2-8]\\d{7}|9(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))))|(?:1(?:0(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))|[1-8]\\d{7}|9(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))))|[2-8]\\d{8}|9(?:0(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))|[1-8]\\d{7}|9(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))))|(?:1(?:0(?:0(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))|[1-8]\\d{7}|9(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))))|[1-8]\\d{8}|9(?:0(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))|[1-8]\\d{7}|9(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))))|[2-3]\\d{9}|4(?:0(?:0(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))))|[1-8]\\d{7}|9(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-8]\\d{6}|9(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))))|1(?:0(?:0(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-8]\\d{5}|9(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))))|[1-1]\\d{6}|2(?:0(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-8]\\d{4}|9(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))))|[1-3]\\d{5}|4(?:0(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-8]\\d{3}|9(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9])))|[1-3]\\d{4}|4(?:0(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-8]\\d{2}|9(?:0[0-9]|[1-8]\\d{1}|9[0-9]))|[1-3]\\d{3}|4(?:0(?:0[0-9]|[1-8]\\d{1}|9[0-9])|[1-7]\\d{2}|800)))))))))(?:\\.0+)?)$"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "local-command-input",
"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": "at",
"inputRole": "local-control",
"transform": "identity",
"purpose": "local-command-input",
"contributors": [
"at"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 0,
"maximum": 4102444800,
"parsedValueSchema": {
"type": "integer",
"minimum": 0,
"maximum": 4102444800
}
}
}
],
"files": [
{
"name": "secret-input",
"contract": {
"format": "single-line-secret",
"maxBytes": 4096,
"regularFile": true,
"trim": true,
"reader": "secrets.js:readSecretFile",
"type": "single-line-secret",
"exclusive": false,
"mode": null,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"minLength": 1,
"pattern": "^\\s*\\S[^\\r\\n]*\\s*$"
},
"uses": [
{
"flag": "secret-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Webhook signing secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^\\s*whsec_[A-Za-z0-9+/]{43}=\\s*$",
"description": "One whsec_ secret with a base64 SHA-256 digest. The CLI trims surrounding whitespace before validating it."
}
}
]
},
{
"name": "json-object",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readJsonObject",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"type": "object",
"additionalProperties": true
},
{
"type": "array",
"items": true
}
]
}
},
"uses": [
{
"flag": "headers",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Webhook signature headers",
"description": "JSON object of HTTP headers. A successful verification needs svix-id and svix-signature non-empty strings and svix-timestamp as a non-negative integer or digit string; names are matched case-insensitively. Additional header keys and JSON values are ignored by verification.",
"type": "object",
"additionalProperties": true,
"patternProperties": {
"^[sS][vV][iI][xX]-[iI][dD]$": {
"type": "string",
"minLength": 1
},
"^[sS][vV][iI][xX]-[tT][iI][mM][eE][sS][tT][aA][mM][pP]$": {
"oneOf": [
{
"type": "integer",
"minimum": 0,
"maximum": 999999999999
},
{
"type": "string",
"pattern": "^\\d{1,12}$"
}
]
},
"^[sS][vV][iI][xX]-[sS][iI][gG][nN][aA][tT][uU][rR][eE]$": {
"type": "string",
"minLength": 1,
"pattern": "(?:^|\\s)v1,[A-Za-z0-9+/]{43}=(?:\\s|$)"
}
}
}
}
]
},
{
"name": "webhook-body",
"contract": {
"format": "bytes",
"maxBytes": 1048576,
"regularFile": true,
"reader": "files.js:readBoundedFile",
"type": "bytes",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"contentEncoding": "base64"
},
"uses": [
{
"flag": "body",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Raw webhook request body bytes",
"description": "Exact raw request-body bytes represented as base64. Verification signs these bytes unchanged; do not parse and reserialize the webhook JSON. The CLI accepts up to 1 MiB.",
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "application/octet-stream"
}
}
]
}
],
"wire": {
"method": null,
"route": null,
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
This is a local computation: no API request is made. The exact raw body bytes are signed; re-serializing JSON changes the signature.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"valid": {
"type": "boolean"
},
"timestampAgeSeconds": {
"type": "integer"
}
},
"required": [],
"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 successful response",
"value": {
"status": "success",
"data": {
"valid": true,
"timestampAgeSeconds": 0
}
}
}
]
{
"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
}
}
