Account and personal settings
Akedly CLI command reference: Account and personal settings.
company show
Read your company profile.
When to use
- Read your company profile.
How it works
- Sends GET to /users/company/:userID; the CLI prints the backend response without reshaping it.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Returns the current company profile fields; an update changes only supplied fields and none clears a supplied text field.
Next steps
- Read back changed state with its show/list command.
Usage
akedly company show
Inspect this command offline before preparing an operation:
akedly describe "company show" --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: company_profile:read.
Server request: GET /users/company/:userID.
Owner parameters resolved from the CLI Login: userID.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Read your company profile.
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 usage
Read your company profile
akedly company show
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"name": "Northwind Demo",
"description": "Synthetic example company",
"website": "https://example.invalid",
"logo": null
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly company show
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": "/users/company/:userID",
"query": {},
"body": {},
"ownerParams": [
"userID"
]
}
}
Response contract
data is the account company profile subdocument. name, description, website, or logo may be omitted when unset; logo is maintained by upload-logo.
The CLI returns the API response without reshaping it.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"name": {
"type": [
"string",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"website": {
"type": [
"string",
"null"
]
},
"logo": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "A profile field that has never been set can be absent; a stored value is a string.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"name": "Northwind Demo",
"description": "Synthetic example company",
"website": "https://example.invalid",
"logo": null
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
company update
Edit only supplied company fields.
When to use
- Edit only supplied company fields. none clears a field. Limits: name 120, description 2000 and website 500 characters.
How it works
- Sends PUT to /users/company/:userID; the CLI prints the backend response without reshaping it.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Returns the current company profile fields; an update changes only supplied fields and none clears a supplied text field.
Next steps
- Read back changed state with its show/list command.
Usage
akedly company update
Inspect this command offline before preparing an operation:
akedly describe "company update" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--name | text | — | Command-specific option; see the full command details below. Manifest body field: name. |
--description | text | — | Command-specific option; see the full command details below. Manifest body field: description. |
--website | text | — | Command-specific option; see the full command details below. Manifest body field: website. |
--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: company_profile:update.
Server request: PUT /users/company/:userID.
Owner parameters resolved from the CLI Login: userID.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Edit only supplied company fields. none clears a field. Limits: name 120, description 2000 and website 500 characters.
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 usage
Edit only supplied company fields
akedly company update --name Example
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"name": "Northwind Demo",
"description": "Synthetic example company",
"website": "https://example.invalid",
"logo": null
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly company update --name Example
akedly company update --website none
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "name",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "name",
"transform": "none-empty",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
]
},
{
"name": "description",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "description",
"transform": "none-empty",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
]
},
{
"name": "website",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "website",
"transform": "none-empty",
"role": "body",
"input": "website",
"contributors": [
"website"
]
}
]
},
{
"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": "PUT",
"route": "/users/company/:userID",
"query": {},
"body": {
"name": [
{
"path": "name",
"transform": "none-empty",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
],
"description": [
{
"path": "description",
"transform": "none-empty",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
],
"website": [
{
"path": "website",
"transform": "none-empty",
"role": "body",
"input": "website",
"contributors": [
"website"
]
}
]
},
"ownerParams": [
"userID"
]
}
}
Response contract
data is the account company profile subdocument. name, description, website, or logo may be omitted when unset; logo is maintained by upload-logo.
The CLI returns the API response without reshaping it.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"name": {
"type": [
"string",
"null"
]
},
"description": {
"type": [
"string",
"null"
]
},
"website": {
"type": [
"string",
"null"
]
},
"logo": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "A profile field that has never been set can be absent; a stored value is a string.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"name": "Northwind Demo",
"description": "Synthetic example company",
"website": "https://example.invalid",
"logo": null
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
company upload-logo
Upload a PNG, JPEG, GIF or WebP up to 3 MiB; the server requires at least 8x8 pixels.
When to use
- Upload a PNG, JPEG, GIF or WebP up to 3 MiB; the server requires at least 8x8 pixels. Replaces your current company logo immediately; no follow-up update is needed.
How it works
- Sends POST to /cloudinary/uploadCompanyLogo; the CLI prints the backend response without reshaping it.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Upload a PNG, JPEG, GIF or WebP up to 3 MiB; the server requires at least 8x8 pixels. The API response is returned unchanged.
Next steps
- Read back changed state with its show/list command.
Usage
akedly company upload-logo
Inspect this command offline before preparing an operation:
akedly describe "company upload-logo" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--file | text | — | yes | Command-specific option; see the full command details below. Manifest body field: fileURL. |
--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: company_profile:update.
Server request: POST /cloudinary/uploadCompanyLogo.
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
Upload a PNG, JPEG, GIF or WebP up to 3 MiB; the server requires at least 8x8 pixels. Replaces your current company logo immediately; no follow-up update is needed.
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 usage
Upload a PNG, JPEG, GIF or WebP up to 3 MiB; the server requires at least 8x8 pixels
akedly company upload-logo --file logo.png
Input file: logo.png (base64).
iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAEUlEQVR4nGPQyNuCFTEMLQkAGahSgZLbNF8AAAAASUVORK5CYII=
Illustrative response (sensitive values omitted):
{
"status": "success",
"message": "Logo uploaded successfully",
"data": {
"url": "https://res.cloudinary.com/example/image/upload/company-logo.png"
}
}
Synthetic illustration only; not executed. The file fixture is an 8×8 PNG accepted by the logo image contract.
Additional command forms
akedly company upload-logo --file logo.png
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": "file",
"schema": {
"type": "string"
},
"required": true,
"input": {
"role": "input-file",
"contract": "logo-image",
"requestTargets": [
{
"role": "body",
"path": "fileURL",
"transform": "image-base64"
}
],
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Logo image file contents",
"description": "Binary logo bytes represented here as base64. The CLI reads the file directly, identifies its actual magic bytes, and accepts PNG, JPEG, GIF or WebP up to 3 MiB; the server also requires an image at least 8 by 8 pixels.",
"type": "string",
"contentEncoding": "base64",
"anyOf": [
{
"contentMediaType": "image/png"
},
{
"contentMediaType": "image/jpeg"
},
{
"contentMediaType": "image/gif"
},
{
"contentMediaType": "image/webp"
}
]
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"anyOf": [
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/png"
},
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/jpeg"
},
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/gif"
},
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/webp"
}
]
},
"fileContract": {
"format": "image",
"maxBytes": 3145728,
"regularFile": true,
"types": [
"png",
"jpeg",
"gif",
"webp"
],
"reader": "images.js:logoPlan",
"type": "image",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Logo image file contents",
"description": "Binary logo bytes represented here as base64. The CLI reads the file directly, identifies its actual magic bytes, and accepts PNG, JPEG, GIF or WebP up to 3 MiB; the server also requires an image at least 8 by 8 pixels.",
"type": "string",
"contentEncoding": "base64",
"anyOf": [
{
"contentMediaType": "image/png"
},
{
"contentMediaType": "image/jpeg"
},
{
"contentMediaType": "image/gif"
},
{
"contentMediaType": "image/webp"
}
]
},
"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": "file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "logo-image",
"contributors": [
"file"
]
},
{
"role": "body",
"path": "fileURL",
"transform": "image-base64",
"input": "file",
"contributors": [
"file"
],
"fileContract": "logo-image"
}
]
},
{
"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": "logo-image",
"contract": {
"format": "image",
"maxBytes": 3145728,
"regularFile": true,
"types": [
"png",
"jpeg",
"gif",
"webp"
],
"reader": "images.js:logoPlan",
"type": "image",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"anyOf": [
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/png"
},
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/jpeg"
},
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/gif"
},
{
"type": "string",
"contentEncoding": "base64",
"contentMediaType": "image/webp"
}
]
},
"uses": [
{
"flag": "file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Logo image file contents",
"description": "Binary logo bytes represented here as base64. The CLI reads the file directly, identifies its actual magic bytes, and accepts PNG, JPEG, GIF or WebP up to 3 MiB; the server also requires an image at least 8 by 8 pixels.",
"type": "string",
"contentEncoding": "base64",
"anyOf": [
{
"contentMediaType": "image/png"
},
{
"contentMediaType": "image/jpeg"
},
{
"contentMediaType": "image/gif"
},
{
"contentMediaType": "image/webp"
}
]
}
}
]
}
],
"wire": {
"method": "POST",
"route": "/cloudinary/uploadCompanyLogo",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI returns the backend JSON as received; extra backend fields may appear.
Synthetic values are documentation only.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success",
"error",
"ignored",
"no_match",
"stale"
]
},
"data": {
"type": "object",
"description": "Endpoint-specific backend payload; CLI passes it through unchanged and the API may add fields.",
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data",
"message"
],
"additionalProperties": true
},
"variants": [
{
"title": "HTTP/backend failures are rendered as structured CLI error JSON; they are not represented by this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"message": "Logo uploaded successfully",
"data": {
"url": "https://res.cloudinary.com/example/image/upload/company-logo.png"
}
}
}
]
{
"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
}
}
announcements list
Read your visible announcements.
When to use
- Read your visible announcements.
How it works
- Sends GET to /announcements; the CLI prints the backend response without reshaping it.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Returns visible announcement records with separate nullable seenAt and openedAt markers.
Next steps
- Use mark-seen when the user has viewed the item in their inbox; use mark-opened when they opened its content. read-all records both markers for selected visible items.
Usage
akedly announcements list
Inspect this command offline before preparing an operation:
akedly describe "announcements 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: announcements:read.
Server request: GET /announcements.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Read your visible announcements.
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 usage
Read your visible announcements
akedly announcements list
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": [
{
"_id": "64b000000000000000000001",
"title": "Service update",
"summary": "Synthetic announcement",
"author": "Akedly",
"body": "Example body",
"category": "company",
"tags": [
"service"
],
"media": [
{
"type": "image",
"url": "https://example.invalid/service.png",
"thumbnailUrl": "",
"caption": "Status illustration"
}
],
"cta": [
{
"label": "Read details",
"url": "https://example.invalid/status",
"style": "primary",
"newTab": true
}
],
"pinned": false,
"showAsModal": false,
"publishedAt": "2026-10-01T12:00:00.000Z",
"seenAt": null,
"openedAt": null
}
]
}
Synthetic illustration only; not executed.
Additional command forms
akedly announcements 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": "/announcements",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
Each announcement includes separate seenAt and openedAt timestamps; either can be null. A null marker means the user has not recorded that event.
Audience-invisible or unpublished announcements do not appear.
{
"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"
},
"title": {
"type": "string"
},
"summary": {
"type": "string"
},
"author": {
"type": "string"
},
"body": {
"type": "string"
},
"category": {
"type": "string",
"enum": [
"new_feature",
"improvement",
"bug_fix",
"new_product",
"partnership",
"security",
"deprecation",
"company"
]
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"media": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"image",
"gif",
"video",
"embed"
]
},
"url": {
"type": "string"
},
"thumbnailUrl": {
"type": "string"
},
"caption": {
"type": "string"
}
},
"required": [
"type",
"url",
"thumbnailUrl",
"caption"
],
"additionalProperties": true
}
},
"cta": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"url": {
"type": "string"
},
"style": {
"type": "string",
"enum": [
"primary",
"secondary"
]
},
"newTab": {
"type": "boolean"
}
},
"required": [
"label",
"url",
"style",
"newTab"
],
"additionalProperties": true
}
},
"pinned": {
"type": "boolean"
},
"showAsModal": {
"type": "boolean"
},
"publishedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"seenAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"openedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"required": [
"_id",
"title",
"summary",
"author",
"body",
"category",
"tags",
"media",
"cta",
"pinned",
"showAsModal",
"publishedAt",
"seenAt",
"openedAt"
],
"additionalProperties": true
}
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "HTTP/backend failures are rendered as structured CLI error JSON; they are not represented by this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": [
{
"_id": "64b000000000000000000001",
"title": "Service update",
"summary": "Synthetic announcement",
"author": "Akedly",
"body": "Example body",
"category": "company",
"tags": [
"service"
],
"media": [
{
"type": "image",
"url": "https://example.invalid/service.png",
"thumbnailUrl": "",
"caption": "Status illustration"
}
],
"cta": [
{
"label": "Read details",
"url": "https://example.invalid/status",
"style": "primary",
"newTab": true
}
],
"pinned": false,
"showAsModal": false,
"publishedAt": "2026-10-01T12:00:00.000Z",
"seenAt": null,
"openedAt": null
}
]
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
announcements unread-count
Read your unread announcement count.
When to use
- Read your unread announcement count.
How it works
- Sends GET to /announcements/unread-count; the CLI prints the backend response without reshaping it.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Returns data.unread: the number of visible announcements without a seen marker.
Next steps
- Read back changed state with its show/list command.
Usage
akedly announcements unread-count
Inspect this command offline before preparing an operation:
akedly describe "announcements unread-count" --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: announcements:read.
Server request: GET /announcements/unread-count.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Read your unread announcement count.
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 usage
Read your unread announcement count
akedly announcements unread-count
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"unread": 1
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly announcements unread-count
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": "/announcements/unread-count",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI returns the backend JSON as received; extra backend fields may appear.
Synthetic values are documentation only.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"type": "string"
},
"data": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "HTTP/backend failures are rendered as structured CLI error JSON; they are not represented by this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"unread": 1
}
}
}
]
{
"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
}
}
announcements get
Read a visible announcement by its 24-hex id.
When to use
- Read a visible announcement by its 24-hex id.
How it works
- Sends GET to /announcements/:id; the CLI prints the backend response without reshaping it.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Returns the visible announcement content with separate nullable seenAt and openedAt markers.
Next steps
- Use mark-seen when the user has viewed the item in their inbox; use mark-opened when they opened its content. read-all records both markers for selected visible items.
Usage
akedly announcements get <id>
Inspect this command offline before preparing an operation:
akedly describe "announcements get" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Required positional argument. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--help | boolean switch | — | Show offline command help. |
--output | enum | json | human | Select JSON or human output. |
--fields | dot-paths | — | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: announcements:read.
Server request: GET /announcements/:id.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Read a visible announcement by its 24-hex id.
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 usage
Read a visible announcement by its 24-hex id
akedly announcements get 64b000000000000000000001
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "64b000000000000000000001",
"title": "Service update",
"summary": "Synthetic announcement",
"author": "Akedly",
"body": "Example body",
"category": "company",
"tags": [
"service"
],
"media": [
{
"type": "image",
"url": "https://example.invalid/service.png",
"thumbnailUrl": "",
"caption": "Status illustration"
}
],
"cta": [
{
"label": "Read details",
"url": "https://example.invalid/status",
"style": "primary",
"newTab": true
}
],
"pinned": false,
"showAsModal": false,
"publishedAt": "2026-10-01T12:00:00.000Z",
"seenAt": null,
"openedAt": null
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly announcements get <id>
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/announcements/:id",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
Each announcement includes separate seenAt and openedAt timestamps; either can be null. A null marker means the user has not recorded that event.
Audience-invisible or unpublished announcements do not appear.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"title": {
"type": "string"
},
"summary": {
"type": "string"
},
"author": {
"type": "string"
},
"body": {
"type": "string"
},
"category": {
"type": "string",
"enum": [
"new_feature",
"improvement",
"bug_fix",
"new_product",
"partnership",
"security",
"deprecation",
"company"
]
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"media": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"image",
"gif",
"video",
"embed"
]
},
"url": {
"type": "string"
},
"thumbnailUrl": {
"type": "string"
},
"caption": {
"type": "string"
}
},
"required": [
"type",
"url",
"thumbnailUrl",
"caption"
],
"additionalProperties": true
}
},
"cta": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string"
},
"url": {
"type": "string"
},
"style": {
"type": "string",
"enum": [
"primary",
"secondary"
]
},
"newTab": {
"type": "boolean"
}
},
"required": [
"label",
"url",
"style",
"newTab"
],
"additionalProperties": true
}
},
"pinned": {
"type": "boolean"
},
"showAsModal": {
"type": "boolean"
},
"publishedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"seenAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"openedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"required": [
"_id",
"title",
"summary",
"author",
"body",
"category",
"tags",
"media",
"cta",
"pinned",
"showAsModal",
"publishedAt",
"seenAt",
"openedAt"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "HTTP/backend failures are rendered as structured CLI error JSON; they are not represented by this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"_id": "64b000000000000000000001",
"title": "Service update",
"summary": "Synthetic announcement",
"author": "Akedly",
"body": "Example body",
"category": "company",
"tags": [
"service"
],
"media": [
{
"type": "image",
"url": "https://example.invalid/service.png",
"thumbnailUrl": "",
"caption": "Status illustration"
}
],
"cta": [
{
"label": "Read details",
"url": "https://example.invalid/status",
"style": "primary",
"newTab": true
}
],
"pinned": false,
"showAsModal": false,
"publishedAt": "2026-10-01T12:00:00.000Z",
"seenAt": null,
"openedAt": null
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
announcements mark-seen
Mark this published announcement seen.
When to use
- Mark this published announcement seen.
How it works
- Sets this user’s seenAt marker once; it does not set openedAt.
- Seen is the marker used by unread-count.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Mark this published announcement seen. The API response is returned unchanged.
Next steps
- Read back changed state with its show/list command.
Usage
akedly announcements mark-seen <id>
Inspect this command offline before preparing an operation:
akedly describe "announcements mark-seen" --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. |
--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: announcements:read.
Server request: POST /announcements/:id/seen.
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
Mark this published announcement seen.
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 usage
Mark this published announcement seen
akedly announcements mark-seen 64b000000000000000000001
Illustrative response (sensitive values omitted):
{
"status": "success",
"message": "Marked as seen"
}
Synthetic illustration only; not executed.
Additional command forms
akedly announcements mark-seen <id>
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "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": "/announcements/:id/seen",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI returns the backend JSON as received; extra backend fields may appear.
Synthetic values are documentation only.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"status",
"message"
],
"additionalProperties": true
},
"variants": [
{
"title": "HTTP/backend failures are rendered as structured CLI error JSON; they are not represented by this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"message": "Marked as seen"
}
}
]
{
"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
}
}
announcements mark-opened
Mark this published announcement opened.
When to use
- Mark this published announcement opened.
How it works
- Sets both seenAt and openedAt for this user, with first timestamps preserved.
- Opened therefore also counts as seen; opened and seen remain separate timestamps in announcement records.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Mark this published announcement opened. The API response is returned unchanged.
Next steps
- Read back changed state with its show/list command.
Usage
akedly announcements mark-opened <id>
Inspect this command offline before preparing an operation:
akedly describe "announcements mark-opened" --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. |
--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: announcements:read.
Server request: POST /announcements/:id/opened.
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
Mark this published announcement opened.
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 usage
Mark this published announcement opened
akedly announcements mark-opened 64b000000000000000000001
Illustrative response (sensitive values omitted):
{
"status": "success",
"message": "Marked as opened"
}
Synthetic illustration only; not executed.
Additional command forms
akedly announcements mark-opened <id>
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "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": "/announcements/:id/opened",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI returns the backend JSON as received; extra backend fields may appear.
Synthetic values are documentation only.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"type": "string"
},
"message": {
"type": "string"
}
},
"required": [
"status",
"message"
],
"additionalProperties": true
},
"variants": [
{
"title": "HTTP/backend failures are rendered as structured CLI error JSON; they are not represented by this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"message": "Marked as opened"
}
}
]
{
"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
}
}
announcements read-all
Mark 1 to 1000 comma-separated announcement ids as read.
When to use
- Mark 1 to 1000 comma-separated announcement ids as read. An empty list is refused.
How it works
- For 1–1000 supplied IDs, applies only to announcements visible to this user.
- Sets both seenAt and openedAt for visible IDs that are not already opened; the marked count reports selected visible records without openedAt before the update.
- An empty list is refused by CLI validation.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
Returns data.marked and records both seen and opened for the selected visible announcements; this operation is broader than marking them seen only.
Next steps
- Read back changed state with its show/list command.
Usage
akedly announcements read-all
Inspect this command offline before preparing an operation:
akedly describe "announcements read-all" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--ids | ids | — | yes | Command-specific option; see the full command details below. Manifest body field: ids. |
--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: announcements:read.
Server request: POST /announcements/read-all.
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
Mark 1 to 1000 comma-separated announcement ids as read. An empty list is refused.
Worked examples
Examples are synthetic illustrations, never live account records. Replace resource IDs with your own authorized resources. Secret values are omitted here; redacted examples illustrate the shape and are not schema-validation fixtures.
Synthetic usage
Mark 1 to 1000 comma-separated announcement ids as read
akedly announcements read-all --ids 64b000000000000000000001,64b000000000000000000002
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"marked": 2
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly announcements read-all --ids <id>,<id>
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "ids",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}(?:,[a-fA-F0-9]{24})*$"
},
"required": true,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "ids",
"transform": "split-comma"
}
],
"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": "ids",
"transform": "split-comma",
"input": "ids",
"contributors": [
"ids"
]
}
],
"validation": {
"syntax": "Comma-separated 24-digit hexadecimal object IDs; no empty segments."
}
},
{
"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": "/announcements/read-all",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
read-all sets both seenAt and openedAt, whereas mark-seen sets only seenAt and mark-opened sets both.
data.marked counts selected visible announcements that were not already opened, not a generic unread count.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"success",
"error",
"ignored",
"no_match",
"stale"
]
},
"data": {
"type": "object",
"description": "Endpoint-specific backend payload; CLI passes it through unchanged and the API may add fields.",
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "HTTP/backend failures are rendered as structured CLI error JSON; they are not represented by this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"marked": 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
}
}
passkeys list
See registered passkey users grouped by phone number, including device count and frozen state.
When to use
- Review enrollment footprint or confirm whether a phone currently has registered passkeys.
How it works
- Login-authenticated owner-scoped read returns a paginated aggregation; optional search filters phone numbers.
Prerequisites
- A stored CLI Login for this region with the command permission.
Expected outcome
Rows summarize phone, deviceCount, Active/Frozen status, last use, total use count, enrollment date, and pipeline count.
Next steps
- Use passkeys freeze to pause or resume passkeys for a phone when needed.
Usage
akedly passkeys list
Inspect this command offline before preparing an operation:
akedly describe "passkeys list" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--page | integer | 1 to unbounded | Command-specific option; see the full command details below. Query parameter: page. |
--limit | integer | 1 to unbounded | Command-specific option; see the full command details below. Query parameter: limit. |
--search | text | — | Command-specific option; see the full command details below. Query parameter: search. |
--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: passkeys:read.
Server request: GET /passkey/list/:userID.
Owner parameters resolved from the CLI Login: userID.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Read your registered passkey users.
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 offline fixture
See registered passkey users grouped by phone number, including device count and frozen state.
akedly passkeys list --page 1 --limit 20
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": [
{
"phone": "+201234567890",
"deviceCount": 2,
"status": "Active",
"lastUsedAt": "2026-10-29T12:00:00.000Z",
"totalUseCount": 7,
"enrolledAt": "2026-10-29T12:00:00.000Z",
"pipelineCount": 1
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
},
"message": "Registered passkey users fetched successfully"
}
All IDs, destination, company, dates, callback URL and payload values are synthetic fixtures; example.invalid is reserved for documentation. Do not paste a real OTP, phone, API key, widget Secret, token, or account identifier into an agent test or shared output.
This worked response is illustrative only and was not executed against an account.
Additional command forms
akedly passkeys list
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "page",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]\\d*)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"parsedValueSchema": {
"type": "integer",
"minimum": 1
}
}
},
{
"name": "limit",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]\\d*)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"parsedValueSchema": {
"type": "integer",
"minimum": 1
}
}
},
{
"name": "search",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "search",
"transform": "identity",
"role": "query",
"input": "search",
"contributors": [
"search"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/passkey/list/:userID",
"query": {
"page": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"limit": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"search": [
{
"path": "search",
"transform": "identity",
"role": "query",
"input": "search",
"contributors": [
"search"
]
}
]
},
"body": {},
"ownerParams": [
"userID"
]
}
}
Response contract
Successful response example is synthetic and demonstrates observed field names/types; server-supplied extension fields are preserved.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"phone": {
"type": "string"
},
"deviceCount": {
"type": "integer"
},
"status": {
"enum": [
"Active",
"Frozen"
]
},
"lastUsedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
]
},
"totalUseCount": {
"type": "integer"
},
"enrolledAt": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
]
},
"pipelineCount": {
"type": "integer"
}
},
"required": [
"phone",
"deviceCount",
"status",
"lastUsedAt",
"totalUseCount",
"enrolledAt",
"pipelineCount"
],
"additionalProperties": true
}
},
"pagination": {
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"page": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"totalPages": {
"type": "integer"
}
},
"required": [
"total",
"page",
"limit",
"totalPages"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data",
"pagination",
"message"
],
"additionalProperties": true
},
"variants": [
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"type": "object",
"additionalProperties": true
},
"hint": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"additionalProperties": true
},
"title": "API refusal",
"when": "A non-success HTTP response; CLI reports a structured error on stderr/stdout according to output mode and preserves backend code/message when present.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": [
{
"phone": "+201234567890",
"deviceCount": 2,
"status": "Active",
"lastUsedAt": "2026-10-29T12:00:00.000Z",
"totalUseCount": 7,
"enrolledAt": "2026-10-29T12:00:00.000Z",
"pipelineCount": 1
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
},
"message": "Registered passkey users fetched successfully"
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
passkeys history
Review recent passkey authentication request outcomes and billing fields.
When to use
- Investigate successful/failed passkey verification activity for your account, optionally searching a phone number.
How it works
- Login-authenticated owner-scoped paginated GET returns auth-request records formatted for the history table.
Prerequisites
- A stored CLI Login for this region with the command permission.
Expected outcome
Rows include request ID, phone, status, verification/billing flags, dates, and optional pipeline summary.
Next steps
- Compare billed/status details with account transaction and billing records.
Usage
akedly passkeys history
Inspect this command offline before preparing an operation:
akedly describe "passkeys history" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--page | integer | 1 to unbounded | Command-specific option; see the full command details below. Query parameter: page. |
--limit | integer | 1 to unbounded | Command-specific option; see the full command details below. Query parameter: limit. |
--search | text | — | Command-specific option; see the full command details below. Query parameter: search. |
--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: passkeys:read.
Server request: GET /passkey/history/:userID.
Owner parameters resolved from the CLI Login: userID.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Read your passkey verification history.
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 offline fixture
Review recent passkey authentication request outcomes and billing fields.
akedly passkeys history --page 1 --limit 20
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": [
{
"requestID": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"_id": "65a1b2c3d4e5f67890123456",
"userID": "65a1b2c3d4e5f67890123458",
"phoneNumber": "+201234567890",
"status": "Verified",
"method": "passkey",
"verified": true,
"billingAmount": 0.25,
"billed": true,
"createdAt": "2026-10-29T12:00:00.000Z",
"verificationDate": "2026-10-29T12:00:00.000Z",
"expirationDate": "2026-10-29T12:05:00.000Z",
"pipeline": {
"name": "Example OTP pipeline",
"type": "Test",
"active": true
},
"verificationTimeSeconds": 3,
"isExpired": false
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
},
"message": "Passkey verification history fetched successfully"
}
All IDs, destination, company, dates, callback URL and payload values are synthetic fixtures; example.invalid is reserved for documentation. Do not paste a real OTP, phone, API key, widget Secret, token, or account identifier into an agent test or shared output.
This worked response is illustrative only and was not executed against an account.
Additional command forms
akedly passkeys history
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": "page",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]\\d*)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"parsedValueSchema": {
"type": "integer",
"minimum": 1
}
}
},
{
"name": "limit",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[1-9]\\d*)(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 1,
"parsedValueSchema": {
"type": "integer",
"minimum": 1
}
}
},
{
"name": "search",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "search",
"transform": "identity",
"role": "query",
"input": "search",
"contributors": [
"search"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/passkey/history/:userID",
"query": {
"page": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"limit": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"search": [
{
"path": "search",
"transform": "identity",
"role": "query",
"input": "search",
"contributors": [
"search"
]
}
]
},
"body": {},
"ownerParams": [
"userID"
]
}
}
Response contract
Successful response example is synthetic and demonstrates observed field names/types; server-supplied extension fields are preserved.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"requestID": {
"type": "string"
},
"_id": {
"type": "string"
},
"userID": {
"type": "string"
},
"phoneNumber": {
"type": "string"
},
"status": {
"type": "string"
},
"method": {
"const": "passkey"
},
"verified": {
"type": "boolean"
},
"billingAmount": {
"type": "number"
},
"billed": {
"type": "boolean"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"verificationDate": {
"anyOf": [
{
"type": "string",
"format": "date-time"
},
{
"type": "null"
}
]
},
"expirationDate": {
"type": "string",
"format": "date-time"
},
"pipeline": {
"anyOf": [
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"enum": [
"Live",
"Test"
]
},
"active": {
"type": "boolean"
}
},
"required": [
"name",
"type",
"active"
],
"additionalProperties": true
},
{
"type": "null"
}
]
},
"verificationTimeSeconds": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
]
},
"isExpired": {
"type": "boolean"
}
},
"required": [
"requestID",
"_id",
"userID",
"phoneNumber",
"status",
"method",
"verified",
"billingAmount",
"billed",
"createdAt",
"expirationDate",
"pipeline",
"verificationTimeSeconds",
"isExpired"
],
"additionalProperties": true
}
},
"pagination": {
"type": "object",
"properties": {
"total": {
"type": "integer"
},
"page": {
"type": "integer"
},
"limit": {
"type": "integer"
},
"totalPages": {
"type": "integer"
}
},
"required": [
"total",
"page",
"limit",
"totalPages"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data",
"pagination",
"message"
],
"additionalProperties": true
},
"variants": [
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"type": "object",
"additionalProperties": true
},
"hint": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"additionalProperties": true
},
"title": "API refusal",
"when": "A non-success HTTP response; CLI reports a structured error on stderr/stdout according to output mode and preserves backend code/message when present.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": [
{
"requestID": "3f2504e0-4f89-41d3-9a0c-0305e82c3301",
"_id": "65a1b2c3d4e5f67890123456",
"userID": "65a1b2c3d4e5f67890123458",
"phoneNumber": "+201234567890",
"status": "Verified",
"method": "passkey",
"verified": true,
"billingAmount": 0.25,
"billed": true,
"createdAt": "2026-10-29T12:00:00.000Z",
"verificationDate": "2026-10-29T12:00:00.000Z",
"expirationDate": "2026-10-29T12:05:00.000Z",
"pipeline": {
"name": "Example OTP pipeline",
"type": "Test",
"active": true
},
"verificationTimeSeconds": 3,
"isExpired": false
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
},
"message": "Passkey verification history fetched successfully"
}
},
{
"title": "Synthetic pending history row with no verification date or pipeline",
"value": {
"status": "success",
"data": [
{
"requestID": "e7ce2fd0-0209-41de-9c66-4bb7e524528a",
"_id": "65a1b2c3d4e5f67890123457",
"userID": "65a1b2c3d4e5f67890123458",
"phoneNumber": "+201234567890",
"status": "Pending",
"method": "passkey",
"verified": false,
"billingAmount": 0,
"billed": false,
"createdAt": "2026-10-29T12:00:00.000Z",
"expirationDate": "2026-10-29T12:05:00.000Z",
"pipeline": null,
"verificationTimeSeconds": null,
"isExpired": false
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"totalPages": 1
},
"message": "Passkey verification history fetched successfully"
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
passkeys freeze
Temporarily disable or restore passkey authentication for all passkey credentials matching one phone in your account.
When to use
- Use when a user needs passkeys paused or restored; freezing routes future authentication back to OTP, which may be billable.
How it works
- PATCH owner-scoped body with phone and boolean frozen state; confirmation is required. Repeating the selected state is safe and returns modifiedCount.
Prerequisites
- A stored CLI Login for this region with the command permission.
Expected outcome
Reports normalized phone, requested frozen value, and number of modified credentials.
Next steps
- Check passkeys list and communicate that OTP may be used while frozen.
Usage
akedly passkeys freeze
Inspect this command offline before preparing an operation:
akedly describe "passkeys freeze" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--phone-number | text | — | yes | Command-specific option; see the full command details below. Manifest body field: phoneNumber. |
--frozen | enum | on | off | yes | Command-specific option; see the full command details below. Manifest body field: frozen. |
--dry-run | boolean switch | — | no | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--yes | boolean switch | — | no | Confirm this action where consent is required. |
--help | boolean switch | — | no | Show offline command help. |
--output | enum | json | human | no | Select JSON or human output. |
--fields | dot-paths | — | no | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: passkeys:update.
Server request: PATCH /passkey/freeze.
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
Freeze or unfreeze all passkeys for one end-user phone. --yes is required for either value; freezing sends the user back to potentially billable OTP.
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 offline fixture
Temporarily disable or restore passkey authentication for all passkey credentials matching one phone in your account.
akedly passkeys freeze --phone-number +201234567890 --frozen on --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"phoneNumber": "+201234567890",
"frozen": true,
"modifiedCount": 2
},
"message": "Passkeys frozen successfully"
}
All IDs, destination, company, dates, callback URL and payload values are synthetic fixtures; example.invalid is reserved for documentation. Do not paste a real OTP, phone, API key, widget Secret, token, or account identifier into an agent test or shared output.
This worked response is illustrative only and was not executed against an account.
Additional command forms
akedly passkeys freeze --phone-number <phone> --frozen on --yes
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "phone-number",
"schema": {
"type": "string"
},
"required": true,
"wire": [
{
"path": "phoneNumber",
"transform": "identity",
"role": "body",
"input": "phone-number",
"contributors": [
"phone-number"
]
}
]
},
{
"name": "frozen",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": true,
"wire": [
{
"path": "frozen",
"transform": "identity",
"role": "body",
"input": "frozen",
"contributors": [
"frozen"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
},
{
"name": "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": [],
"wire": {
"method": "PATCH",
"route": "/passkey/freeze",
"query": {},
"body": {
"phone-number": [
{
"path": "phoneNumber",
"transform": "identity",
"role": "body",
"input": "phone-number",
"contributors": [
"phone-number"
]
}
],
"frozen": [
{
"path": "frozen",
"transform": "identity",
"role": "body",
"input": "frozen",
"contributors": [
"frozen"
]
}
]
},
"ownerParams": []
}
}
Response contract
Successful response example is synthetic and demonstrates observed field names/types; server-supplied extension fields are preserved.
Freezing makes future verification fall back to OTP, which may be billed.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"phoneNumber": {
"type": "string"
},
"frozen": {
"type": "boolean"
},
"modifiedCount": {
"type": "integer"
}
},
"required": [
"phoneNumber",
"frozen",
"modifiedCount"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data",
"message"
],
"additionalProperties": true
},
"variants": [
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"type": "object",
"additionalProperties": true
},
"hint": {
"type": "string"
}
},
"required": [
"code",
"message"
],
"additionalProperties": true
},
"title": "API refusal",
"when": "A non-success HTTP response; CLI reports a structured error on stderr/stdout according to output mode and preserves backend code/message when present.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"phoneNumber": "+201234567890",
"frozen": true,
"modifiedCount": 2
},
"message": "Passkeys frozen successfully"
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
personal-notifications show
Inspect this signed-in person’s platform billing preference, phone setup, email alerts, newsletter preference, and whether platform notifications can currently be delivered.
When to use
- Before enabling billing alerts, determine whether phone verification and account enrollment are complete.
- Check a notification state before diagnosing an unavailable or ineligible delivery path.
How it works
- Reads the current identity-scoped state through GET /platform-notifications/me.
- For an owner, the first read may create the owner contact and default billing subscription; this is a read with that initialization side effect.
- Returns setup, contact, preferences, effective state/reason and capabilities; staff-approved identities are refused with SELF_IDENTITY_REQUIRED.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
A full data object describes the authenticated person and account availability. It does not send a notification.
Next steps
- If contact.state is missing or pending, finish phone setup and account enrollment in the dashboard.
- Use personal-notifications set only when the response capabilities and prerequisites permit the change.
Usage
akedly personal-notifications show
Inspect this command offline before preparing an operation:
akedly describe "personal-notifications show" --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: none declared.
Server request: GET /platform-notifications/me.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Read your personal notification settings. Your first read creates your notification settings if needed. Staff-approved logins are refused with SELF_IDENTITY_REQUIRED.
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 usage
Read your personal notification settings
akedly personal-notifications show
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": false
}
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly personal-notifications show
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": "/platform-notifications/me",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
All successful reads and writes return the full serializeSelfState projection. Fields reflect the current account, the logged-in person’s contact and preferences, effective delivery readiness, and available actions.
The account owner’s email alerts are not editable and owner email notifications remain enabled; teammate state can be role-restricted.
Phone numbers in examples are fictional; actual contact.phoneE164 is returned by the API.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"account": {
"type": "object",
"properties": {
"displayName": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"displayName"
]
},
"setup": {
"type": "object",
"properties": {
"disclosureVersion": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"disclosureVersion"
]
},
"email": {
"type": "object",
"properties": {
"address": {
"type": [
"string",
"null"
]
},
"role": {
"type": "string",
"enum": [
"owner",
"teammate"
]
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string",
"enum": [
"active",
"none",
"pending",
"declined",
"deactivated"
]
},
"catalog": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"desc": {
"type": "string"
},
"category": {
"type": "string"
},
"defaultOn": {
"type": "boolean"
},
"permission": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"additionalProperties": true,
"required": [
"key",
"label",
"desc",
"category",
"defaultOn",
"permission"
]
}
},
"types": {
"type": "object",
"properties": {
"reports": {
"type": "boolean"
},
"quotaUsage": {
"type": "boolean"
},
"quotaExpiration": {
"type": "boolean"
},
"templateApproved": {
"type": "boolean"
},
"templateRejected": {
"type": "boolean"
},
"templateCategoryChanged": {
"type": "boolean"
},
"seatTermExpiring": {
"type": "boolean"
},
"seatsChanged": {
"type": "boolean"
},
"memberJoined": {
"type": "boolean"
},
"memberRemoved": {
"type": "boolean"
},
"securityIncidents": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"reports",
"quotaUsage",
"quotaExpiration",
"templateApproved",
"templateRejected",
"templateCategoryChanged",
"seatTermExpiring",
"seatsChanged",
"memberJoined",
"memberRemoved",
"securityIncidents"
]
},
"lockedTypes": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
},
"additionalProperties": true,
"required": [
"address",
"role",
"editable",
"readOnlyReason",
"status",
"catalog",
"types",
"lockedTypes"
]
},
"newsletter": {
"type": "object",
"properties": {
"subscribed": {
"type": "boolean"
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"subscribed",
"editable",
"readOnlyReason"
]
},
"availability": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"reason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"enabled",
"reason"
]
},
"contact": {
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": [
"missing",
"pending",
"verified"
]
},
"phoneE164": {
"type": [
"string",
"null"
]
},
"phoneGeneration": {
"type": "integer",
"minimum": 0
},
"language": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"verifiedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"personStopped": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"state",
"phoneE164",
"phoneGeneration",
"language",
"verifiedAt",
"personStopped"
]
},
"preference": {
"type": "object",
"properties": {
"category": {
"const": "billing"
},
"enabled": {
"type": "boolean"
},
"source": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"additionalProperties": true,
"required": [
"category",
"enabled",
"source",
"updatedAt"
]
},
"effectiveState": {
"type": "string",
"enum": [
"unavailable",
"ineligible",
"setup_required",
"stopped",
"preference_off",
"sender_unavailable",
"ready"
]
},
"effectiveReason": {
"type": [
"string",
"null"
]
},
"capabilities": {
"type": "object",
"properties": {
"canEnableBilling": {
"type": "boolean"
},
"canDisableBilling": {
"type": "boolean"
},
"canSetLanguage": {
"type": "boolean"
},
"canReenable": {
"type": "boolean"
},
"canStartVerification": {
"type": "boolean"
},
"canChangePhone": {
"type": "boolean"
},
"canEnrollAccount": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"canEnableBilling",
"canDisableBilling",
"canSetLanguage",
"canReenable",
"canStartVerification",
"canChangePhone",
"canEnrollAccount"
]
}
},
"additionalProperties": true,
"required": [
"account",
"setup",
"email",
"newsletter",
"availability",
"contact",
"preference",
"effectiveState",
"effectiveReason",
"capabilities"
],
"description": "Full self-scoped notification state returned by getMe and every successful preference write."
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Read-only states are represented in data.email.editable/readOnlyReason and data.newsletter.editable/readOnlyReason; availability, contact, effectiveState/effectiveReason, and capabilities can vary independently.",
"notes": []
},
{
"title": "The first show can initialize owner contact/subscription records when absent.",
"notes": []
},
{
"title": "Failures use structured error JSON and do not use this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": 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
}
}
personal-notifications set
Change the authenticated person’s platform billing-alert switch, notification language, or both.
When to use
- Turn the platform billing alerts on/off after verifying contact and account enrollment.
- Choose English or Arabic for the platform billing notifications.
How it works
- PATCHes only supplied preference values and returns the full refreshed self state.
- Billing alerts require the platform notification setup/enrollment and eligibility rules; choosing language requires a contact phone.
- It does not perform phone verification or account enrollment.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
The API returns the updated preference within the full self-state projection; effective delivery can remain unavailable even when enabled is true.
Next steps
- Read effectiveState and effectiveReason to distinguish an enabled preference from deliverable notifications.
- Complete phone setup/enrollment in the dashboard if the API reports setup required.
Usage
akedly personal-notifications set
Inspect this command offline before preparing an operation:
akedly describe "personal-notifications set" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--billing-alerts | enum | on | off | Command-specific option; see the full command details below. Manifest body field: category, enabled. |
--language | enum | en | ar | Command-specific option; see the full command details below. Manifest body field: language. |
--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: none declared.
Server request: PATCH /platform-notifications/me/preferences.
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
Set personal billing alerts with --billing-alerts and/or notification language with --language. Requires your own person identity. Finish phone setup and account enrollment on the Personal Notifications page of your Akedly dashboard; the CLI cannot do these steps.
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 usage
Set personal billing alerts with --billing-alerts and/or notification language with --language
akedly personal-notifications set --billing-alerts on --language en
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": false
}
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly personal-notifications set --billing-alerts on --language en
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": "billing-alerts",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "category",
"transform": "billing-category"
},
{
"role": "body",
"path": "enabled",
"transform": "true-false"
}
],
"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": "category",
"transform": "billing-category",
"input": "billing-alerts",
"contributors": [
"billing-alerts"
]
},
{
"role": "body",
"path": "enabled",
"transform": "true-false",
"input": "billing-alerts",
"contributors": [
"billing-alerts"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "language",
"schema": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"required": false,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "language",
"transform": "identity"
}
],
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "body",
"path": "language",
"transform": "identity",
"input": "language",
"contributors": [
"language"
]
}
]
},
{
"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": "/platform-notifications/me/preferences",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
All successful reads and writes return the full serializeSelfState projection. Fields reflect the current account, the logged-in person’s contact and preferences, effective delivery readiness, and available actions.
The account owner’s email alerts are not editable and owner email notifications remain enabled; teammate state can be role-restricted.
Phone numbers in examples are fictional; actual contact.phoneE164 is returned by the API.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"account": {
"type": "object",
"properties": {
"displayName": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"displayName"
]
},
"setup": {
"type": "object",
"properties": {
"disclosureVersion": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"disclosureVersion"
]
},
"email": {
"type": "object",
"properties": {
"address": {
"type": [
"string",
"null"
]
},
"role": {
"type": "string",
"enum": [
"owner",
"teammate"
]
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string",
"enum": [
"active",
"none",
"pending",
"declined",
"deactivated"
]
},
"catalog": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"desc": {
"type": "string"
},
"category": {
"type": "string"
},
"defaultOn": {
"type": "boolean"
},
"permission": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"additionalProperties": true,
"required": [
"key",
"label",
"desc",
"category",
"defaultOn",
"permission"
]
}
},
"types": {
"type": "object",
"properties": {
"reports": {
"type": "boolean"
},
"quotaUsage": {
"type": "boolean"
},
"quotaExpiration": {
"type": "boolean"
},
"templateApproved": {
"type": "boolean"
},
"templateRejected": {
"type": "boolean"
},
"templateCategoryChanged": {
"type": "boolean"
},
"seatTermExpiring": {
"type": "boolean"
},
"seatsChanged": {
"type": "boolean"
},
"memberJoined": {
"type": "boolean"
},
"memberRemoved": {
"type": "boolean"
},
"securityIncidents": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"reports",
"quotaUsage",
"quotaExpiration",
"templateApproved",
"templateRejected",
"templateCategoryChanged",
"seatTermExpiring",
"seatsChanged",
"memberJoined",
"memberRemoved",
"securityIncidents"
]
},
"lockedTypes": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
},
"additionalProperties": true,
"required": [
"address",
"role",
"editable",
"readOnlyReason",
"status",
"catalog",
"types",
"lockedTypes"
]
},
"newsletter": {
"type": "object",
"properties": {
"subscribed": {
"type": "boolean"
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"subscribed",
"editable",
"readOnlyReason"
]
},
"availability": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"reason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"enabled",
"reason"
]
},
"contact": {
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": [
"missing",
"pending",
"verified"
]
},
"phoneE164": {
"type": [
"string",
"null"
]
},
"phoneGeneration": {
"type": "integer",
"minimum": 0
},
"language": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"verifiedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"personStopped": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"state",
"phoneE164",
"phoneGeneration",
"language",
"verifiedAt",
"personStopped"
]
},
"preference": {
"type": "object",
"properties": {
"category": {
"const": "billing"
},
"enabled": {
"type": "boolean"
},
"source": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"additionalProperties": true,
"required": [
"category",
"enabled",
"source",
"updatedAt"
]
},
"effectiveState": {
"type": "string",
"enum": [
"unavailable",
"ineligible",
"setup_required",
"stopped",
"preference_off",
"sender_unavailable",
"ready"
]
},
"effectiveReason": {
"type": [
"string",
"null"
]
},
"capabilities": {
"type": "object",
"properties": {
"canEnableBilling": {
"type": "boolean"
},
"canDisableBilling": {
"type": "boolean"
},
"canSetLanguage": {
"type": "boolean"
},
"canReenable": {
"type": "boolean"
},
"canStartVerification": {
"type": "boolean"
},
"canChangePhone": {
"type": "boolean"
},
"canEnrollAccount": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"canEnableBilling",
"canDisableBilling",
"canSetLanguage",
"canReenable",
"canStartVerification",
"canChangePhone",
"canEnrollAccount"
]
}
},
"additionalProperties": true,
"required": [
"account",
"setup",
"email",
"newsletter",
"availability",
"contact",
"preference",
"effectiveState",
"effectiveReason",
"capabilities"
],
"description": "Full self-scoped notification state returned by getMe and every successful preference write."
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Read-only states are represented in data.email.editable/readOnlyReason and data.newsletter.editable/readOnlyReason; availability, contact, effectiveState/effectiveReason, and capabilities can vary independently.",
"notes": []
},
{
"title": "The first show can initialize owner contact/subscription records when absent.",
"notes": []
},
{
"title": "Failures use structured error JSON and do not use this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": 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
}
}
personal-notifications set-email-alerts
Change selected email alert types for the currently signed-in teammate.
When to use
- A teammate wants to opt into or out of specific account event emails.
How it works
- Reads current full state, changes only named alert keys, then writes all catalog types and returns refreshed self state.
- Because it writes the full alert map after a prior read, a simultaneous dashboard update can be overwritten.
- Account owners always receive these alerts; staff organizations and unavailable/no-address identities are read-only.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
The email.types map in returned data reflects the resulting per-person alert choices; the response is not a simple updated marker.
Next steps
- Inspect email.types, email.lockedTypes, email.editable, and email.readOnlyReason.
- If a type is locked by role, ask an organization admin about the role permission; do not retry the same denied update.
Usage
akedly personal-notifications set-email-alerts
Inspect this command offline before preparing an operation:
akedly describe "personal-notifications set-email-alerts" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--types | text | — | yes | Command-specific option; see the full command details below. Manifest body field: types. |
--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: none declared.
Server request: PUT /platform-notifications/me/email-alerts.
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
Change your own email alerts with --types, a comma-separated list of word=on|off pairs using the words below. The CLI reads your current preferences, changes only the words you give and writes all 11 alerts back, so a dashboard change made at the same moment can be overwritten. Dry-run shows <current> for the alerts you did not give. Unknown or repeated words and values other than on or off are refused before any request. The owner always receives every email alert; owner changes are refused with OWNER_ALWAYS_RECEIVES. This changes your own alerts when you are a logged-in teammate, not another person's. Read-only preferences are refused with their existing error code before any update. Words for --types, each followed by the name it prints as in results: reports (reports), quota-usage (quotaUsage), credit-plan-running-out (quotaExpiration), template-approvals (templateApproved), template-rejections (templateRejected), template-category-changes (templateCategoryChanged), seats-term-ending (seatTermExpiring), seat-changes (seatsChanged), member-joined (memberJoined), member-left (memberRemoved), security-incidents (securityIncidents).
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 usage
Change your own email alerts with --types, a comma-separated list of word=on|off pairs using the words below
akedly personal-notifications set-email-alerts --types quota-usage=on,member-left=off
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": false
}
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly personal-notifications set-email-alerts --types quota-usage=on,member-left=off
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": "types",
"schema": {
"type": "string"
},
"required": true,
"input": {
"role": "handler-built",
"targets": [
{
"role": "body",
"path": "types",
"transform": "notification-types"
}
],
"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": "types",
"transform": "notification-types",
"input": "types",
"contributors": [
"types"
]
}
]
},
{
"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": "PUT",
"route": "/platform-notifications/me/email-alerts",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
All successful reads and writes return the full serializeSelfState projection. Fields reflect the current account, the logged-in person’s contact and preferences, effective delivery readiness, and available actions.
The account owner’s email alerts are not editable and owner email notifications remain enabled; teammate state can be role-restricted.
Phone numbers in examples are fictional; actual contact.phoneE164 is returned by the API.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"account": {
"type": "object",
"properties": {
"displayName": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"displayName"
]
},
"setup": {
"type": "object",
"properties": {
"disclosureVersion": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"disclosureVersion"
]
},
"email": {
"type": "object",
"properties": {
"address": {
"type": [
"string",
"null"
]
},
"role": {
"type": "string",
"enum": [
"owner",
"teammate"
]
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string",
"enum": [
"active",
"none",
"pending",
"declined",
"deactivated"
]
},
"catalog": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"desc": {
"type": "string"
},
"category": {
"type": "string"
},
"defaultOn": {
"type": "boolean"
},
"permission": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"additionalProperties": true,
"required": [
"key",
"label",
"desc",
"category",
"defaultOn",
"permission"
]
}
},
"types": {
"type": "object",
"properties": {
"reports": {
"type": "boolean"
},
"quotaUsage": {
"type": "boolean"
},
"quotaExpiration": {
"type": "boolean"
},
"templateApproved": {
"type": "boolean"
},
"templateRejected": {
"type": "boolean"
},
"templateCategoryChanged": {
"type": "boolean"
},
"seatTermExpiring": {
"type": "boolean"
},
"seatsChanged": {
"type": "boolean"
},
"memberJoined": {
"type": "boolean"
},
"memberRemoved": {
"type": "boolean"
},
"securityIncidents": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"reports",
"quotaUsage",
"quotaExpiration",
"templateApproved",
"templateRejected",
"templateCategoryChanged",
"seatTermExpiring",
"seatsChanged",
"memberJoined",
"memberRemoved",
"securityIncidents"
]
},
"lockedTypes": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
},
"additionalProperties": true,
"required": [
"address",
"role",
"editable",
"readOnlyReason",
"status",
"catalog",
"types",
"lockedTypes"
]
},
"newsletter": {
"type": "object",
"properties": {
"subscribed": {
"type": "boolean"
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"subscribed",
"editable",
"readOnlyReason"
]
},
"availability": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"reason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"enabled",
"reason"
]
},
"contact": {
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": [
"missing",
"pending",
"verified"
]
},
"phoneE164": {
"type": [
"string",
"null"
]
},
"phoneGeneration": {
"type": "integer",
"minimum": 0
},
"language": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"verifiedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"personStopped": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"state",
"phoneE164",
"phoneGeneration",
"language",
"verifiedAt",
"personStopped"
]
},
"preference": {
"type": "object",
"properties": {
"category": {
"const": "billing"
},
"enabled": {
"type": "boolean"
},
"source": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"additionalProperties": true,
"required": [
"category",
"enabled",
"source",
"updatedAt"
]
},
"effectiveState": {
"type": "string",
"enum": [
"unavailable",
"ineligible",
"setup_required",
"stopped",
"preference_off",
"sender_unavailable",
"ready"
]
},
"effectiveReason": {
"type": [
"string",
"null"
]
},
"capabilities": {
"type": "object",
"properties": {
"canEnableBilling": {
"type": "boolean"
},
"canDisableBilling": {
"type": "boolean"
},
"canSetLanguage": {
"type": "boolean"
},
"canReenable": {
"type": "boolean"
},
"canStartVerification": {
"type": "boolean"
},
"canChangePhone": {
"type": "boolean"
},
"canEnrollAccount": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"canEnableBilling",
"canDisableBilling",
"canSetLanguage",
"canReenable",
"canStartVerification",
"canChangePhone",
"canEnrollAccount"
]
}
},
"additionalProperties": true,
"required": [
"account",
"setup",
"email",
"newsletter",
"availability",
"contact",
"preference",
"effectiveState",
"effectiveReason",
"capabilities"
],
"description": "Full self-scoped notification state returned by getMe and every successful preference write."
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Read-only states are represented in data.email.editable/readOnlyReason and data.newsletter.editable/readOnlyReason; availability, contact, effectiveState/effectiveReason, and capabilities can vary independently.",
"notes": []
},
{
"title": "The first show can initialize owner contact/subscription records when absent.",
"notes": []
},
{
"title": "Failures use structured error JSON and do not use this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": 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": {
"MEMBERSHIP_UNAVAILABLE": "Try again in a moment.",
"RECIPIENT_UNAVAILABLE": "Try again in a moment.",
"OWNER_ALWAYS_RECEIVES": "Account owners always receive every alert type; change types for a teammate login.",
"STAFF_ORG": "Staff accounts have no editable email alerts.",
"NO_ADDRESS": "Add an email address to your account in the dashboard first."
},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
Own recovery guidance
| Code | Hint |
|---|---|
| MEMBERSHIP_UNAVAILABLE | Try again in a moment. |
| RECIPIENT_UNAVAILABLE | Try again in a moment. |
| OWNER_ALWAYS_RECEIVES | Account owners always receive every alert type; change types for a teammate login. |
| STAFF_ORG | Staff accounts have no editable email alerts. |
| NO_ADDRESS | Add an email address to your account in the dashboard first. |
personal-notifications set-newsletter
Set the signed-in person’s Akedly newsletter subscription preference.
When to use
- The person wants to subscribe or unsubscribe their own email address from the newsletter.
How it works
- Updates newsletter subscription for the current identity’s resolved address and returns the full refreshed self state.
- This is a personal preference and cannot be changed for another teammate.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
data.newsletter.subscribed reflects the current preference along with the rest of self state.
Next steps
- Read back newsletter.subscribed and editable.
Usage
akedly personal-notifications set-newsletter
Inspect this command offline before preparing an operation:
akedly describe "personal-notifications set-newsletter" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--newsletter | enum | on | off | yes | Command-specific option; see the full command details below. Manifest body field: subscribed. |
--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: none declared.
Server request: PUT /platform-notifications/me/newsletter.
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
Set your own newsletter subscription.
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 usage
Set your own newsletter subscription
akedly personal-notifications set-newsletter --newsletter on
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": true,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": false
}
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly personal-notifications set-newsletter --newsletter on
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": "newsletter",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": true,
"wire": [
{
"path": "subscribed",
"transform": "identity",
"role": "body",
"input": "newsletter",
"contributors": [
"newsletter"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "PUT",
"route": "/platform-notifications/me/newsletter",
"query": {},
"body": {
"newsletter": [
{
"path": "subscribed",
"transform": "identity",
"role": "body",
"input": "newsletter",
"contributors": [
"newsletter"
]
}
]
},
"ownerParams": []
}
}
Response contract
All successful reads and writes return the full serializeSelfState projection. Fields reflect the current account, the logged-in person’s contact and preferences, effective delivery readiness, and available actions.
The account owner’s email alerts are not editable and owner email notifications remain enabled; teammate state can be role-restricted.
Phone numbers in examples are fictional; actual contact.phoneE164 is returned by the API.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"account": {
"type": "object",
"properties": {
"displayName": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"displayName"
]
},
"setup": {
"type": "object",
"properties": {
"disclosureVersion": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"disclosureVersion"
]
},
"email": {
"type": "object",
"properties": {
"address": {
"type": [
"string",
"null"
]
},
"role": {
"type": "string",
"enum": [
"owner",
"teammate"
]
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string",
"enum": [
"active",
"none",
"pending",
"declined",
"deactivated"
]
},
"catalog": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"desc": {
"type": "string"
},
"category": {
"type": "string"
},
"defaultOn": {
"type": "boolean"
},
"permission": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"additionalProperties": true,
"required": [
"key",
"label",
"desc",
"category",
"defaultOn",
"permission"
]
}
},
"types": {
"type": "object",
"properties": {
"reports": {
"type": "boolean"
},
"quotaUsage": {
"type": "boolean"
},
"quotaExpiration": {
"type": "boolean"
},
"templateApproved": {
"type": "boolean"
},
"templateRejected": {
"type": "boolean"
},
"templateCategoryChanged": {
"type": "boolean"
},
"seatTermExpiring": {
"type": "boolean"
},
"seatsChanged": {
"type": "boolean"
},
"memberJoined": {
"type": "boolean"
},
"memberRemoved": {
"type": "boolean"
},
"securityIncidents": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"reports",
"quotaUsage",
"quotaExpiration",
"templateApproved",
"templateRejected",
"templateCategoryChanged",
"seatTermExpiring",
"seatsChanged",
"memberJoined",
"memberRemoved",
"securityIncidents"
]
},
"lockedTypes": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
},
"additionalProperties": true,
"required": [
"address",
"role",
"editable",
"readOnlyReason",
"status",
"catalog",
"types",
"lockedTypes"
]
},
"newsletter": {
"type": "object",
"properties": {
"subscribed": {
"type": "boolean"
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"subscribed",
"editable",
"readOnlyReason"
]
},
"availability": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"reason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"enabled",
"reason"
]
},
"contact": {
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": [
"missing",
"pending",
"verified"
]
},
"phoneE164": {
"type": [
"string",
"null"
]
},
"phoneGeneration": {
"type": "integer",
"minimum": 0
},
"language": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"verifiedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"personStopped": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"state",
"phoneE164",
"phoneGeneration",
"language",
"verifiedAt",
"personStopped"
]
},
"preference": {
"type": "object",
"properties": {
"category": {
"const": "billing"
},
"enabled": {
"type": "boolean"
},
"source": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"additionalProperties": true,
"required": [
"category",
"enabled",
"source",
"updatedAt"
]
},
"effectiveState": {
"type": "string",
"enum": [
"unavailable",
"ineligible",
"setup_required",
"stopped",
"preference_off",
"sender_unavailable",
"ready"
]
},
"effectiveReason": {
"type": [
"string",
"null"
]
},
"capabilities": {
"type": "object",
"properties": {
"canEnableBilling": {
"type": "boolean"
},
"canDisableBilling": {
"type": "boolean"
},
"canSetLanguage": {
"type": "boolean"
},
"canReenable": {
"type": "boolean"
},
"canStartVerification": {
"type": "boolean"
},
"canChangePhone": {
"type": "boolean"
},
"canEnrollAccount": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"canEnableBilling",
"canDisableBilling",
"canSetLanguage",
"canReenable",
"canStartVerification",
"canChangePhone",
"canEnrollAccount"
]
}
},
"additionalProperties": true,
"required": [
"account",
"setup",
"email",
"newsletter",
"availability",
"contact",
"preference",
"effectiveState",
"effectiveReason",
"capabilities"
],
"description": "Full self-scoped notification state returned by getMe and every successful preference write."
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Read-only states are represented in data.email.editable/readOnlyReason and data.newsletter.editable/readOnlyReason; availability, contact, effectiveState/effectiveReason, and capabilities can vary independently.",
"notes": []
},
{
"title": "The first show can initialize owner contact/subscription records when absent.",
"notes": []
},
{
"title": "Failures use structured error JSON and do not use this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": true,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": 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
}
}
personal-notifications reenable
Restore this person’s platform notification capability after they previously stopped notifications.
When to use
- The person has asked to re-enable platform notifications after opting out.
How it works
- Re-enables the existing identity-scoped contact/subscription state and returns full refreshed self state.
- It does not send an immediate message; future eligible notifications may resume.
Prerequisites
- A valid CLI login and required command permission. Organization-admin access is required where enforced.
- Any targeted resource must already exist.
Expected outcome
The full self-state shows current contact stop state, preference, effective state/reason, and capability flags.
Next steps
- Verify effectiveState and capabilities.canReenable; complete setup or address the reported eligibility reason if delivery is not ready.
Usage
akedly personal-notifications reenable
Inspect this command offline before preparing an operation:
akedly describe "personal-notifications reenable" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--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: login. Permission: none declared.
Server request: POST /platform-notifications/me/reenable.
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
Turn notifications back on for yourself. Finish phone setup and account enrollment on the Personal Notifications page of your Akedly dashboard; the CLI cannot do these steps.
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 usage
Turn notifications back on for yourself
akedly personal-notifications reenable
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": false
}
}
}
Synthetic illustration only; not executed.
Additional command forms
akedly personal-notifications reenable
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": "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": "/platform-notifications/me/reenable",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
All successful reads and writes return the full serializeSelfState projection. Fields reflect the current account, the logged-in person’s contact and preferences, effective delivery readiness, and available actions.
The account owner’s email alerts are not editable and owner email notifications remain enabled; teammate state can be role-restricted.
Phone numbers in examples are fictional; actual contact.phoneE164 is returned by the API.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"account": {
"type": "object",
"properties": {
"displayName": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"displayName"
]
},
"setup": {
"type": "object",
"properties": {
"disclosureVersion": {
"type": "string"
}
},
"additionalProperties": true,
"required": [
"disclosureVersion"
]
},
"email": {
"type": "object",
"properties": {
"address": {
"type": [
"string",
"null"
]
},
"role": {
"type": "string",
"enum": [
"owner",
"teammate"
]
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
},
"status": {
"type": "string",
"enum": [
"active",
"none",
"pending",
"declined",
"deactivated"
]
},
"catalog": {
"type": "array",
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"label": {
"type": "string"
},
"desc": {
"type": "string"
},
"category": {
"type": "string"
},
"defaultOn": {
"type": "boolean"
},
"permission": {
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
}
},
"additionalProperties": true,
"required": [
"key",
"label",
"desc",
"category",
"defaultOn",
"permission"
]
}
},
"types": {
"type": "object",
"properties": {
"reports": {
"type": "boolean"
},
"quotaUsage": {
"type": "boolean"
},
"quotaExpiration": {
"type": "boolean"
},
"templateApproved": {
"type": "boolean"
},
"templateRejected": {
"type": "boolean"
},
"templateCategoryChanged": {
"type": "boolean"
},
"seatTermExpiring": {
"type": "boolean"
},
"seatsChanged": {
"type": "boolean"
},
"memberJoined": {
"type": "boolean"
},
"memberRemoved": {
"type": "boolean"
},
"securityIncidents": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"reports",
"quotaUsage",
"quotaExpiration",
"templateApproved",
"templateRejected",
"templateCategoryChanged",
"seatTermExpiring",
"seatsChanged",
"memberJoined",
"memberRemoved",
"securityIncidents"
]
},
"lockedTypes": {
"type": "object",
"additionalProperties": {
"type": "boolean"
}
}
},
"additionalProperties": true,
"required": [
"address",
"role",
"editable",
"readOnlyReason",
"status",
"catalog",
"types",
"lockedTypes"
]
},
"newsletter": {
"type": "object",
"properties": {
"subscribed": {
"type": "boolean"
},
"editable": {
"type": "boolean"
},
"readOnlyReason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"subscribed",
"editable",
"readOnlyReason"
]
},
"availability": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean"
},
"reason": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": true,
"required": [
"enabled",
"reason"
]
},
"contact": {
"type": "object",
"properties": {
"state": {
"type": "string",
"enum": [
"missing",
"pending",
"verified"
]
},
"phoneE164": {
"type": [
"string",
"null"
]
},
"phoneGeneration": {
"type": "integer",
"minimum": 0
},
"language": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"verifiedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"personStopped": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"state",
"phoneE164",
"phoneGeneration",
"language",
"verifiedAt",
"personStopped"
]
},
"preference": {
"type": "object",
"properties": {
"category": {
"const": "billing"
},
"enabled": {
"type": "boolean"
},
"source": {
"type": [
"string",
"null"
]
},
"updatedAt": {
"type": [
"string",
"null"
],
"format": "date-time"
}
},
"additionalProperties": true,
"required": [
"category",
"enabled",
"source",
"updatedAt"
]
},
"effectiveState": {
"type": "string",
"enum": [
"unavailable",
"ineligible",
"setup_required",
"stopped",
"preference_off",
"sender_unavailable",
"ready"
]
},
"effectiveReason": {
"type": [
"string",
"null"
]
},
"capabilities": {
"type": "object",
"properties": {
"canEnableBilling": {
"type": "boolean"
},
"canDisableBilling": {
"type": "boolean"
},
"canSetLanguage": {
"type": "boolean"
},
"canReenable": {
"type": "boolean"
},
"canStartVerification": {
"type": "boolean"
},
"canChangePhone": {
"type": "boolean"
},
"canEnrollAccount": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"canEnableBilling",
"canDisableBilling",
"canSetLanguage",
"canReenable",
"canStartVerification",
"canChangePhone",
"canEnrollAccount"
]
}
},
"additionalProperties": true,
"required": [
"account",
"setup",
"email",
"newsletter",
"availability",
"contact",
"preference",
"effectiveState",
"effectiveReason",
"capabilities"
],
"description": "Full self-scoped notification state returned by getMe and every successful preference write."
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"title": "Read-only states are represented in data.email.editable/readOnlyReason and data.newsletter.editable/readOnlyReason; availability, contact, effectiveState/effectiveReason, and capabilities can vary independently.",
"notes": []
},
{
"title": "The first show can initialize owner contact/subscription records when absent.",
"notes": []
},
{
"title": "Failures use structured error JSON and do not use this success schema.",
"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": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"account": {
"displayName": "Demo account"
},
"setup": {
"disclosureVersion": "s3-2026-09"
},
"email": {
"address": "member@example.invalid",
"role": "teammate",
"editable": true,
"readOnlyReason": null,
"status": "active",
"catalog": [
{
"key": "reports",
"label": "Reports",
"desc": "Scheduled usage and delivery reports",
"category": "Reports & analytics",
"defaultOn": true,
"permission": "reports:read"
},
{
"key": "quotaUsage",
"label": "Quota usage alerts",
"desc": "When usage crosses a threshold",
"category": "Usage & billing",
"defaultOn": true,
"permission": "billing:read"
}
],
"types": {
"reports": true,
"quotaUsage": true,
"quotaExpiration": false,
"templateApproved": false,
"templateRejected": false,
"templateCategoryChanged": false,
"seatTermExpiring": false,
"seatsChanged": false,
"memberJoined": false,
"memberRemoved": false,
"securityIncidents": false
},
"lockedTypes": {}
},
"newsletter": {
"subscribed": false,
"editable": true,
"readOnlyReason": null
},
"availability": {
"enabled": true,
"reason": null
},
"contact": {
"state": "verified",
"phoneE164": "+201234567890",
"phoneGeneration": 0,
"language": "en",
"verifiedAt": "2026-10-01T12:00:00.000Z",
"personStopped": false
},
"preference": {
"category": "billing",
"enabled": true,
"source": "owner_default",
"updatedAt": "2026-10-01T12:00:00.000Z"
},
"effectiveState": "ready",
"effectiveReason": null,
"capabilities": {
"canEnableBilling": true,
"canDisableBilling": true,
"canSetLanguage": true,
"canReenable": false,
"canStartVerification": false,
"canChangePhone": true,
"canEnrollAccount": 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
}
}
