api-keys list
Review key labels, prefixes, scope and status without revealing one-time secrets.
When to use
- Use before editing, rotating or revoking a key.
How it works
- Reads the account’s API-key inventory.
Expected outcome
A list of key metadata is returned.
Next steps
- Use create for a new credential; never use a displayed prefix as a secret.
Usage
akedly api-keys list
Inspect this command offline before preparing an operation:
akedly describe "api-keys 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: api_keys:read.
Server request: GET /api-keys.
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 API keys.
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 workflow
Review key labels, prefixes, scope and status without revealing one-time secrets.
akedly api-keys list
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": [
{
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1
}
],
"meta": {
"maxActiveKeys": 10,
"scopeEnforcementMode": "enforce",
"scopeEditingEnabled": true,
"scopeTargets": {
"pipelines": [],
"totpPipelines": []
},
"budgetEnforcementMode": "enforce",
"budgetEditingEnabled": true,
"budgetConfigUnavailable": false
}
}
Inventory omits secret material; response metadata is extensible.
Additional command forms
akedly api-keys 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": "/api-keys",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
Inventory omits secret material; response metadata is extensible.
scope is a serialized object with mode, pipelineIds and typed targets; it is not a string.
{
"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",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
}
},
"required": [
"id",
"label",
"prefix",
"replacementRecoveryAvailable",
"origin",
"status",
"secretViewState",
"scope",
"scopeSummary",
"spendLimitEgp",
"settledUsageEgp",
"pendingUsageEgp",
"budgetUpdatedAt",
"createdAt",
"updatedAt",
"lastUsedAt",
"revokeEffectiveAt",
"revokedAt",
"configVersion"
],
"additionalProperties": true
}
},
"meta": {
"type": "object",
"properties": {
"maxActiveKeys": {
"type": "integer"
},
"scopeEnforcementMode": {
"type": "string"
},
"scopeEditingEnabled": {
"type": "boolean"
},
"scopeTargets": {
"type": "object",
"properties": {
"pipelines": {
"type": "array"
},
"totpPipelines": {
"type": "array"
}
},
"required": [],
"additionalProperties": true
},
"budgetEnforcementMode": {
"type": "string"
},
"budgetEditingEnabled": {
"type": "boolean"
},
"budgetConfigUnavailable": {
"type": "boolean"
}
},
"required": [],
"additionalProperties": true
}
},
"required": [
"status",
"data",
"meta"
],
"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 response",
"value": {
"status": "success",
"data": [
{
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1
}
],
"meta": {
"maxActiveKeys": 10,
"scopeEnforcementMode": "enforce",
"scopeEditingEnabled": true,
"scopeTargets": {
"pipelines": [],
"totpPipelines": []
},
"budgetEnforcementMode": "enforce",
"budgetEditingEnabled": true,
"budgetConfigUnavailable": 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
}
}
api-keys create
Issue a scoped credential for an integration, optionally saving its one-time secret to a protected local sink.
When to use
- Use when an integration needs its own credential or a rotation needs overlap.
How it works
- Issues a key using a generated or supplied idempotency key; after transient ambiguity it retries with the same key and may recover the secret once. Secret is returned once or delivered to
--secret-file/--save.
Expected outcome
New key metadata and a one-time secret delivery result are returned.
Next steps
- Store the secret in the integration’s secret manager, then revoke an old key only after deployment.
Usage
akedly api-keys create
Inspect this command offline before preparing an operation:
akedly describe "api-keys create" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--label | text | — | Command-specific option; see the full command details below. Manifest body field: label. |
--scope | enum | all | selected | Command-specific option; see the full command details below. Manifest body field: scope.mode, scope.pipelineIds, scope.targets. |
--pipeline-ids | ids | — | Command-specific option; see the full command details below. Manifest body field: scope.mode, scope.pipelineIds, scope.targets. |
--totp-pipeline-ids | ids | — | Command-specific option; see the full command details below. Manifest body field: scope.mode, scope.pipelineIds, scope.targets. |
--spend-limit-egp | text | — | Command-specific option; see the full command details below. Manifest body field: spendLimitEgp. |
--idempotency-key | text | — | Command-specific option; see the full command details below. Manifest body field: idempotencyKey. |
--replaces-api-key-id | id | — | Command-specific option; see the full command details below. Manifest body field: replacesApiKeyId. |
--save | boolean switch | — | Save the API key locally for Sending Commands and omit the Secret from output. |
--secret-file | text | — | Write the secret to a new mode 0600 file and omit it from output; an existing path is refused. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--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: api_keys:create.
Server request: POST /api-keys.
Consent
--yes unless --scope selected is given, and always with --replaces-api-key-id or --save. --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, --save.
Full command details
Issue an API key. --replaces-api-key-id replaces an existing key and its old Secret stops immediately. To rotate without downtime, create a new key, deploy its Secret, then run akedly api-keys revoke <id> --grace-minutes <n> --yes. api-keys create repeats its own issue automatically: after a network error or HTTP 5xx it re-sends once with the same idempotency key, and if the response carries no Secret it calls recover-secret once; if that still fails, follow the error hint to recover by hand. Without --save or --secret-file the Secret is printed once in the output; use --secret-file or --save when output is logged or read by an agent. Creating a key that can use every pipeline (the default scope all) needs --yes, and so does --save, which makes the new key this region's saved default for Sending Commands; a --scope selected key without --save does not.
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 workflow
Issue a scoped credential for an integration, optionally saving its one-time secret to a protected local sink.
akedly api-keys create --label 'Synthetic integration' --idempotency-key synthetic-idempotency-0001 --save --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1,
"secretOperation": {
"operation": "issue",
"apiKeyId": "507f1f77bcf86cd799439014",
"configVersion": 1,
"requestKey": "synthetic-idempotency-0001"
},
"saved": {
"region": "egypt",
"prefix": "aaaaaaaa",
"storage": "file",
"default": true
}
}
}
Fake secret marker is synthetic and not usable. Prefer --secret-file or --save if command output may be logged. Idempotency key is generated if omitted.
A synthetic 64-character hexadecimal secret is omitted from output because --save is present; it is not a real credential.
Additional command forms
akedly api-keys create --label terminal --yes
akedly api-keys create --label terminal --save --yes
akedly api-keys create --label ci --scope selected --pipeline-ids <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": "label",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "label",
"transform": "trim"
}
],
"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": "label",
"transform": "trim",
"input": "label",
"contributors": [
"label"
]
}
]
},
{
"name": "scope",
"schema": {
"type": "string",
"enum": [
"all",
"selected"
]
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "scope.mode",
"transform": "scope-mode"
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids"
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets"
}
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-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": "scope.mode",
"transform": "scope-mode",
"input": "scope",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids",
"input": "scope",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets",
"input": "scope",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
}
]
},
{
"name": "pipeline-ids",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}(?:,[a-fA-F0-9]{24})*$"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "scope.mode",
"transform": "scope-mode"
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids"
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets"
}
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-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": "scope.mode",
"transform": "scope-mode",
"input": "pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids",
"input": "pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets",
"input": "pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
}
],
"validation": {
"syntax": "Comma-separated 24-digit hexadecimal object IDs; no empty segments."
}
},
{
"name": "totp-pipeline-ids",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}(?:,[a-fA-F0-9]{24})*$"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "scope.mode",
"transform": "scope-mode"
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids"
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets"
}
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-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": "scope.mode",
"transform": "scope-mode",
"input": "totp-pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids",
"input": "totp-pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets",
"input": "totp-pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
}
],
"validation": {
"syntax": "Comma-separated 24-digit hexadecimal object IDs; no empty segments."
}
},
{
"name": "spend-limit-egp",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "spendLimitEgp",
"transform": "none-null-number"
}
],
"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": "spendLimitEgp",
"transform": "none-null-number",
"input": "spend-limit-egp",
"contributors": [
"spend-limit-egp"
]
}
]
},
{
"name": "idempotency-key",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "idempotencyKey",
"transform": "string"
}
],
"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": "idempotencyKey",
"transform": "string",
"input": "idempotency-key",
"contributors": [
"idempotency-key"
]
}
]
},
{
"name": "replaces-api-key-id",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}$"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "replacesApiKeyId",
"transform": "string"
}
],
"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": "replacesApiKeyId",
"transform": "string",
"input": "replaces-api-key-id",
"contributors": [
"replaces-api-key-id"
]
}
],
"validation": {
"syntax": "24-digit hexadecimal object ID."
}
},
{
"name": "save",
"schema": {
"type": "boolean"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "local-control",
"purpose": "region-keyed-api-key-store",
"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": "save",
"inputRole": "local-control",
"transform": "identity",
"purpose": "region-keyed-api-key-store",
"contributors": [
"save"
]
}
]
},
{
"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": "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"
]
}
]
}
],
"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": "/api-keys",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
Fake secret marker is synthetic and not usable. Prefer --secret-file or --save if command output may be logged. Idempotency key is generated if omitted.
Without --save or --secret-file, stdout includes the actual 64-character hexadecimal secret. With either sink, secret is omitted and saved/secretFile metadata is returned.
With --save, secret is removed from stdout and data.saved is {region,prefix,storage,default}. Without --save or --secret-file, the actual 64-hex secret is present in stdout. Use a private sink when output can be logged.
scope is a serialized object with mode, pipelineIds and typed targets; it is not a string.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"revokeOutcome": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Secret returned to stdout when no sink is selected",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secret",
"secretOperation"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
},
{
"title": "Secret saved to a local sink",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secretOperation",
"saved"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
},
{
"title": "Secret written to a private file",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secretOperation",
"secretFile"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
}
],
"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 saved-key response",
"value": {
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1,
"secretOperation": {
"operation": "issue",
"apiKeyId": "507f1f77bcf86cd799439014",
"configVersion": 1,
"requestKey": "synthetic-idempotency-0001"
},
"saved": {
"region": "egypt",
"prefix": "aaaaaaaa",
"storage": "file",
"default": true
}
}
}
}
]
{
"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
}
}
api-keys update
Change an existing key’s label, allowed pipeline scope or spending cap.
When to use
- Use to narrow a key after integration scope changes or adjust an authorized cap.
How it works
- Reads current config version when needed, then applies a version-checked patch; budget reduction requires explicit confirmation.
Expected outcome
Updated key settings are returned; the secret does not change.
Next steps
- List the key to confirm configuration.
Usage
akedly api-keys update <id>
Inspect this command offline before preparing an operation:
akedly describe "api-keys 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. |
--label | text | — | Command-specific option; see the full command details below. Manifest body field: label. |
--scope | enum | all | selected | Command-specific option; see the full command details below. Manifest body field: scope.mode, scope.pipelineIds, scope.targets. |
--pipeline-ids | ids | — | Command-specific option; see the full command details below. Manifest body field: scope.mode, scope.pipelineIds, scope.targets. |
--totp-pipeline-ids | ids | — | Command-specific option; see the full command details below. Manifest body field: scope.mode, scope.pipelineIds, scope.targets. |
--spend-limit-egp | text | — | Command-specific option; see the full command details below. Manifest body field: spendLimitEgp. |
--confirm-budget-reduction | boolean switch | — | Command-specific option; see the full command details below. Manifest body field: confirmBudgetReduction, expectedBudgetUpdatedAt. |
--expected-config-version | integer | 1 to unbounded | Command-specific option; see the full command details below. Manifest body field: expectedConfigVersion. |
--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: api_keys:update.
Server request: PATCH /api-keys/:apiKeyId.
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
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
Edit API-key settings.
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 workflow
Change an existing key’s label, allowed pipeline scope or spending cap.
akedly api-keys update 507f1f77bcf86cd799439013 --label 'Synthetic updated'
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic updated",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 2
}
}
Version conflicts require listing current keys and retrying against the current version.
Additional command forms
akedly api-keys update 0123456789abcdef01234567 --label Renamed
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": "apiKeyId",
"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": "label",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "label",
"transform": "trim"
}
],
"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": "label",
"transform": "trim",
"input": "label",
"contributors": [
"label"
]
}
]
},
{
"name": "scope",
"schema": {
"type": "string",
"enum": [
"all",
"selected"
]
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "scope.mode",
"transform": "scope-mode"
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids"
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets"
}
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-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": "scope.mode",
"transform": "scope-mode",
"input": "scope",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids",
"input": "scope",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets",
"input": "scope",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
}
]
},
{
"name": "pipeline-ids",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}(?:,[a-fA-F0-9]{24})*$"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "scope.mode",
"transform": "scope-mode"
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids"
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets"
}
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-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": "scope.mode",
"transform": "scope-mode",
"input": "pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids",
"input": "pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets",
"input": "pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
}
],
"validation": {
"syntax": "Comma-separated 24-digit hexadecimal object IDs; no empty segments."
}
},
{
"name": "totp-pipeline-ids",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}(?:,[a-fA-F0-9]{24})*$"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "scope.mode",
"transform": "scope-mode"
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids"
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets"
}
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-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": "scope.mode",
"transform": "scope-mode",
"input": "totp-pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.pipelineIds",
"transform": "scope-pipeline-ids",
"input": "totp-pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
},
{
"role": "body",
"path": "scope.targets",
"transform": "scope-targets",
"input": "totp-pipeline-ids",
"contributors": [
"pipeline-ids",
"scope",
"totp-pipeline-ids"
],
"inputs": {
"scope": "scope",
"pipelineIds": "pipeline-ids",
"totpPipelineIds": "totp-pipeline-ids"
}
}
],
"validation": {
"syntax": "Comma-separated 24-digit hexadecimal object IDs; no empty segments."
}
},
{
"name": "spend-limit-egp",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "spendLimitEgp",
"transform": "none-null-number"
}
],
"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": "spendLimitEgp",
"transform": "none-null-number",
"input": "spend-limit-egp",
"contributors": [
"spend-limit-egp"
]
}
]
},
{
"name": "confirm-budget-reduction",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "confirmBudgetReduction",
"transform": "present"
},
{
"role": "body",
"path": "expectedBudgetUpdatedAt",
"transform": "current-budget-version"
}
],
"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": "confirmBudgetReduction",
"transform": "present",
"input": "confirm-budget-reduction",
"contributors": [
"confirm-budget-reduction"
]
},
{
"role": "body",
"path": "expectedBudgetUpdatedAt",
"transform": "current-budget-version",
"input": "confirm-budget-reduction",
"contributors": [
"confirm-budget-reduction"
]
}
]
},
{
"name": "expected-config-version",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]\\d*)(?:\\.0+)?)$"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "expectedConfigVersion",
"transform": "number"
}
],
"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": "expectedConfigVersion",
"transform": "number",
"input": "expected-config-version",
"contributors": [
"expected-config-version"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"parsedValueSchema": {
"type": "integer",
"minimum": 1
}
}
},
{
"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": "/api-keys/:apiKeyId",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
Version conflicts require listing current keys and retrying against the current version.
scope is a serialized object with mode, pipelineIds and typed targets; it is not a string.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"revokeOutcome": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
}
},
"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 response",
"value": {
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic updated",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 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
}
}
api-keys revoke
Stop an API key from authenticating, optionally allowing a chosen grace period for cutover.
When to use
- Use after a replacement secret is deployed or immediately during compromise response.
How it works
- Requires explicit grace minutes and
--yes; sends revocation request.
Expected outcome
Revocation state and effective grace metadata are returned.
Next steps
- Verify the integration has moved to a replacement key.
Usage
akedly api-keys revoke <id>
Inspect this command offline before preparing an operation:
akedly describe "api-keys revoke" --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. |
--grace-minutes | integer | 0 to 1440 | yes | Command-specific option; see the full command details below. Manifest body field: graceMinutes. |
--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: api_keys:delete.
Server request: POST /api-keys/:apiKeyId/revoke.
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
Revoke an API key with an explicit grace period.
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 workflow
Stop an API key from authenticating, optionally allowing a chosen grace period for cutover.
akedly api-keys revoke 507f1f77bcf86cd799439013 --grace-minutes 0 --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "revoked",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": "2030-01-01T00:00:00Z",
"configVersion": 1,
"revokeOutcome": "updated"
}
}
Irreversible credential effect; retries after ambiguity should be preceded by listing keys.
Additional command forms
akedly api-keys revoke <id> --grace-minutes 0 --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": "apiKeyId",
"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": "grace-minutes",
"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-3]\\d{2}|4(?:0[0-9]|[1-3]\\d{1}|40))))(?:\\.0+)?)$"
},
"required": true,
"wire": [
{
"path": "graceMinutes",
"transform": "identity",
"role": "body",
"input": "grace-minutes",
"contributors": [
"grace-minutes"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 0,
"maximum": 1440,
"parsedValueSchema": {
"type": "integer",
"minimum": 0,
"maximum": 1440
}
}
},
{
"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-keys/:apiKeyId/revoke",
"query": {},
"body": {
"grace-minutes": [
{
"path": "graceMinutes",
"transform": "identity",
"role": "body",
"input": "grace-minutes",
"contributors": [
"grace-minutes"
]
}
]
},
"ownerParams": []
}
}
Response contract
Irreversible credential effect; retries after ambiguity should be preceded by listing keys.
scope is a serialized object with mode, pipelineIds and typed targets; it is not a string.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"revokeOutcome": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
}
},
"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 response",
"value": {
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "revoked",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": "2030-01-01T00:00:00Z",
"configVersion": 1,
"revokeOutcome": "updated"
}
}
}
]
{
"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
}
}
api-keys recover-secret
Recover the one-time secret for a recently issued key whose initial delivery was interrupted.
When to use
- Use the exact idempotency key or key id from the issuance result/error.
How it works
- Calls the recovery endpoint once and sends the returned secret to the selected output sink.
Expected outcome
The one-time secret is returned or saved.
Next steps
- If recovery is unavailable, revoke the key and issue a replacement.
Usage
akedly api-keys recover-secret
Inspect this command offline before preparing an operation:
akedly describe "api-keys recover-secret" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--idempotency-key | text | — | Command-specific option; see the full command details below. Manifest body field: idempotencyKey. |
--api-key-id | id | — | Command-specific option; see the full command details below. Manifest body field: apiKeyId. |
--save | boolean switch | — | Save the API key locally for Sending Commands and omit the Secret from output. |
--secret-file | text | — | Write the secret to a new mode 0600 file and omit it from output; an existing path is refused. |
--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: api_keys:create.
Server request: POST /api-keys/recover-secret.
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, --save.
Full command details
Recover a one-time API-key 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 workflow
Recover the one-time secret for a recently issued key whose initial delivery was interrupted.
akedly api-keys recover-secret --idempotency-key synthetic-idempotency-0001 --save
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1,
"secretOperation": {
"operation": "recover",
"apiKeyId": "507f1f77bcf86cd799439014",
"configVersion": 1,
"requestKey": "synthetic-idempotency-0001"
},
"saved": {
"region": "egypt",
"prefix": "aaaaaaaa",
"storage": "file",
"default": true
}
}
}
Never place an actual secret in a human guide or transcript; synthetic marker shown.
A synthetic 64-character hexadecimal secret is omitted from output because --save is present; it is not a real credential.
Additional command forms
akedly api-keys recover-secret --idempotency-key cli-example-idem-0001
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": "idempotency-key",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "idempotencyKey",
"transform": "string"
}
],
"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": "idempotencyKey",
"transform": "string",
"input": "idempotency-key",
"contributors": [
"idempotency-key"
]
}
]
},
{
"name": "api-key-id",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}$"
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "apiKeyId",
"transform": "string"
}
],
"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": "apiKeyId",
"transform": "string",
"input": "api-key-id",
"contributors": [
"api-key-id"
]
}
],
"validation": {
"syntax": "24-digit hexadecimal object ID."
}
},
{
"name": "save",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "region-keyed-api-key-store",
"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": "save",
"inputRole": "local-control",
"transform": "identity",
"purpose": "region-keyed-api-key-store",
"contributors": [
"save"
]
}
]
},
{
"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"
]
}
]
}
],
"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": "/api-keys/recover-secret",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
Never place an actual secret in a human guide or transcript; synthetic marker shown.
Without --save or --secret-file, stdout includes the actual 64-character hexadecimal secret. With either sink, secret is omitted and saved/secretFile metadata is returned.
With --save, secret is removed from stdout and data.saved is {region,prefix,storage,default}. Without --save or --secret-file, the actual 64-hex secret is present in stdout. Use a private sink when output can be logged.
scope is a serialized object with mode, pipelineIds and typed targets; it is not a string.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"revokeOutcome": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Secret returned to stdout when no sink is selected",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secret",
"secretOperation"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
},
{
"title": "Secret saved to a local sink",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secretOperation",
"saved"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
},
{
"title": "Secret written to a private file",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secretOperation",
"secretFile"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
}
],
"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 saved-key response",
"value": {
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1,
"secretOperation": {
"operation": "recover",
"apiKeyId": "507f1f77bcf86cd799439014",
"configVersion": 1,
"requestKey": "synthetic-idempotency-0001"
},
"saved": {
"region": "egypt",
"prefix": "aaaaaaaa",
"storage": "file",
"default": true
}
}
}
}
]
{
"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
}
}
api-keys claim
Claim the one-time secret for a key that exists but has not yet been viewed.
When to use
- Use only to first-view an unseen migrated or signup-created key eligible for transition viewing.
How it works
- Posts claim for a key id and delivers the returned one-time secret to requested sink.
Prerequisites
- Backend origin must be legacy-migrated or signup-dual; ordinary newly issued keys are not claimable.
Expected outcome
The secret is shown once or saved locally.
Next steps
- Copy it into the integration’s secure secret store immediately.
Usage
akedly api-keys claim <id>
Inspect this command offline before preparing an operation:
akedly describe "api-keys claim" --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. |
--save | boolean switch | — | Save the API key locally for Sending Commands and omit the Secret from output. |
--secret-file | text | — | Write the secret to a new mode 0600 file and omit it from output; an existing path is refused. |
--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: api_keys:execute.
Server request: POST /api-keys/:apiKeyId/transition-view.
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, --save.
Full command details
Claim an unseen API-key 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 workflow
Claim the one-time secret for a key that exists but has not yet been viewed.
akedly api-keys claim 507f1f77bcf86cd799439014 --save
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1,
"secretOperation": {
"operation": "claim",
"apiKeyId": "507f1f77bcf86cd799439014",
"configVersion": 1,
"requestKey": null
},
"saved": {
"region": "egypt",
"prefix": "aaaaaaaa",
"storage": "file",
"default": true
}
}
}
Synthetic secret marker is not a valid credential.
A synthetic 64-character hexadecimal secret is omitted from output because --save is present; it is not a real credential.
This is an eligible unseen legacy-migrated key synthetic example; ordinary newly issued keys cannot be claimed.
Additional command forms
akedly api-keys claim <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": "apiKeyId",
"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": "save",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "region-keyed-api-key-store",
"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": "save",
"inputRole": "local-control",
"transform": "identity",
"purpose": "region-keyed-api-key-store",
"contributors": [
"save"
]
}
]
},
{
"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"
]
}
]
}
],
"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": "/api-keys/:apiKeyId/transition-view",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
Synthetic secret marker is not a valid credential.
Without --save or --secret-file, stdout includes the actual 64-character hexadecimal secret. With either sink, secret is omitted and saved/secretFile metadata is returned.
With --save, secret is removed from stdout and data.saved is {region,prefix,storage,default}. Without --save or --secret-file, the actual 64-hex secret is present in stdout. Use a private sink when output can be logged.
scope is a serialized object with mode, pipelineIds and typed targets; it is not a string.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"revokeOutcome": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Secret returned to stdout when no sink is selected",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"secret": {
"type": "string",
"pattern": "^[a-fA-F0-9]{64}$"
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secret",
"secretOperation"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
},
{
"title": "Secret saved to a local sink",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"saved": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"storage": {
"type": "string"
},
"default": {
"const": true
}
},
"required": [
"region",
"prefix",
"storage",
"default"
],
"additionalProperties": true
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secretOperation",
"saved"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
},
{
"title": "Secret written to a private file",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": [
"string",
"null"
]
},
"label": {
"type": "string"
},
"prefix": {
"type": [
"string",
"null"
]
},
"replacesApiKeyId": {
"type": [
"string",
"null"
]
},
"replacementRecoveryAvailable": {
"type": "boolean"
},
"origin": {
"type": "string"
},
"status": {
"type": "string"
},
"secretViewState": {
"type": "string"
},
"scope": {
"type": "object",
"properties": {
"mode": {
"enum": [
"all",
"selected"
]
},
"pipelineIds": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "object",
"properties": {
"kind": {
"enum": [
"pipeline",
"totpPipeline"
]
},
"id": {
"type": "string"
}
},
"required": [
"kind",
"id"
],
"additionalProperties": true
}
}
},
"required": [
"mode",
"pipelineIds",
"targets"
],
"additionalProperties": true
},
"scopeSummary": {
"type": "string"
},
"spendLimitEgp": {
"type": [
"number",
"null"
]
},
"settledUsageEgp": {
"type": "number"
},
"pendingUsageEgp": {
"type": "number"
},
"budgetUpdatedAt": {
"type": [
"string",
"null"
]
},
"createdAt": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
]
},
"lastUsedAt": {
"type": [
"string",
"null"
]
},
"revokeEffectiveAt": {
"type": [
"string",
"null"
]
},
"revokedAt": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"secretOperation": {
"type": "object",
"properties": {
"operation": {
"type": "string"
},
"apiKeyId": {
"type": [
"string",
"null"
]
},
"configVersion": {
"type": "integer"
},
"requestKey": {
"type": [
"string",
"null"
]
}
},
"required": [
"operation",
"apiKeyId",
"configVersion",
"requestKey"
],
"additionalProperties": true
},
"secretFile": {
"type": "string"
}
},
"required": [
"id",
"label",
"prefix",
"status",
"scope",
"secretOperation",
"secretFile"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": []
}
],
"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 saved-key response",
"value": {
"status": "success",
"data": {
"id": "507f1f77bcf86cd799439014",
"label": "Synthetic integration",
"prefix": "aaaaaaaa",
"replacesApiKeyId": null,
"replacementRecoveryAvailable": false,
"origin": "customer",
"status": "active",
"secretViewState": "viewed",
"scope": {
"mode": "all",
"pipelineIds": [],
"targets": []
},
"scopeSummary": "All pipelines",
"spendLimitEgp": null,
"settledUsageEgp": 0,
"pendingUsageEgp": 0,
"budgetUpdatedAt": null,
"createdAt": "2030-01-01T00:00:00Z",
"updatedAt": "2030-01-01T00:00:00Z",
"lastUsedAt": null,
"revokeEffectiveAt": null,
"revokedAt": null,
"configVersion": 1,
"secretOperation": {
"operation": "claim",
"apiKeyId": "507f1f77bcf86cd799439014",
"configVersion": 1,
"requestKey": null
},
"saved": {
"region": "egypt",
"prefix": "aaaaaaaa",
"storage": "file",
"default": true
}
}
}
}
]
{
"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
}
}
api-keys use
Select a previously saved API key as the default key for sending commands, or import one from a local secret file.
When to use
- Use when multiple saved keys exist or a key was provisioned outside this CLI.
How it works
- Validates a secret file against its eight-character prefix when provided; otherwise finds an already saved key. Updates only local API-key selection.
Prerequisites
- Without
--secret-file, this prefix must already identify a saved key for the selected region. With--secret-file, its 64-hex secret must match the prefix.
Expected outcome
The selected prefix and local save metadata are returned.
Next steps
- Run an API command that uses API-key auth to use the selected key.
Usage
akedly api-keys use <prefix>
Inspect this command offline before preparing an operation:
akedly describe "api-keys use" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <prefix> | ^[a-fA-F0-9]{8}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--secret-file | text | — | Command-specific option; see the full command details below. |
--ksa | boolean switch | — | Select the KSA deployment. |
--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: 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
Select or import a saved API key.
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 workflow
Select a previously saved API key as the default key for sending commands, or import one from a local secret file.
akedly api-keys use aaaaaaaa
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"region": "egypt",
"prefix": "aaaaaaaa",
"default": true
}
}
No network request occurs; a secret-file input is sensitive and must remain local.
Import a synthetic API key from a local file and select it as default
Import a separately provisioned key into the local store without sending it to Akedly, then make its prefix the default for API-key-authenticated commands.
akedly api-keys use a1b2c3d4 --secret-file ./synthetic-api-key.secret
Input file: synthetic-api-key.secret (text).
[REDACTED — use your privately saved secret file]
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"region": "egypt",
"prefix": "a1b2c3d4",
"storage": "file",
"default": true
}
}
This is a synthetic, unissued key-shaped fixture; it is not a credential. The local reader accepts a 64-character hexadecimal secret and requires its first eight characters to match the positional prefix.
The command reads and stores this value locally and makes no network request. `storage` may be `keychain` when an OS keychain is available; `file` is the synthetic fallback result shown here.
The fixture contains fake secret material. Do not render its contents in a human guide or publish it as an example key.
Additional command forms
akedly api-keys use <prefix>
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": "prefix",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{8}$",
"mapsToRouteParameter": "prefix",
"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": "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": "Saved API key secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^\\s*[a-fA-F0-9]{64}\\s*$",
"description": "One 64-hex API-key Secret. The CLI trims surrounding whitespace, then requires this exact shape and checks it matches the selected saved key."
},
"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": "Saved API key secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^\\s*[a-fA-F0-9]{64}\\s*$",
"description": "One 64-hex API-key Secret. The CLI trims surrounding whitespace, then requires this exact shape and checks it matches the selected saved key."
},
"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": "ksa",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "ksa",
"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": "ksa",
"inputRole": "local-control",
"transform": "identity",
"purpose": "ksa",
"contributors": [
"ksa"
]
}
]
},
{
"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": "Saved API key secret file contents",
"type": "string",
"minLength": 1,
"maxLength": 4096,
"pattern": "^\\s*[a-fA-F0-9]{64}\\s*$",
"description": "One 64-hex API-key Secret. The CLI trims surrounding whitespace, then requires this exact shape and checks it matches the selected saved key."
}
}
]
}
],
"wire": {
"method": null,
"route": null,
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
No network request occurs; a secret-file input is sensitive and must remain local.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"prefix": {
"type": "string",
"pattern": "^[a-fA-F0-9]{8}$"
},
"default": {
"const": true
},
"storage": {
"type": "string"
}
},
"required": [
"region",
"prefix",
"default"
],
"additionalProperties": true
}
},
"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 local default selection",
"value": {
"status": "success",
"data": {
"region": "egypt",
"prefix": "aaaaaaaa",
"default": true
}
}
}
]
{
"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
}
}
