widgets list
Browse the account’s widgets to find a widget ID, status, or display name.
When to use
- Use for a quick inventory or before opening a particular widget.
How it works
- Reads widgets with optional pagination, status and search filters.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Widget rows returned by the server.
Next steps
- Use the 24-hex _id with widgets get/update, or the public widgetId with widgets analytics.
Usage
akedly widgets list
Inspect this command offline before preparing an operation:
akedly describe "widgets 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. |
--status | enum | active | inactive | suspended | Command-specific option; see the full command details below. Query parameter: status. |
--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: widgets:read.
Server request: GET /widgets.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
List widgets; inactive widgets are hidden unless --status is supplied. Read-only logins are allowed. For a returned widget, this read may disable an expired dev-mode window and delete passkeys enrolled with its Test Pair phone.
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 widgets list workflow
Browse the account’s widgets to find a widget ID, status, or display name.
akedly widgets list --page 1
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"widgets": [
{
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "active"
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"pages": 1
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets 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": "status",
"schema": {
"type": "string",
"enum": [
"active",
"inactive",
"suspended"
]
},
"required": false,
"wire": [
{
"path": "status",
"transform": "identity",
"role": "query",
"input": "status",
"contributors": [
"status"
]
}
]
},
{
"name": "search",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "search",
"transform": "identity",
"role": "query",
"input": "search",
"contributors": [
"search"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/widgets",
"query": {
"page": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"limit": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"status": [
{
"path": "status",
"transform": "identity",
"role": "query",
"input": "status",
"contributors": [
"status"
]
}
],
"search": [
{
"path": "search",
"transform": "identity",
"role": "query",
"input": "search",
"contributors": [
"search"
]
}
]
},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"widgets": {
"type": "array"
},
"pagination": {
"type": "object"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"widgets": [
{
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "active"
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 20,
"pages": 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
}
}
widgets list-filtered
Find widgets by pipeline, status, name, branding or creation range, including attached widgets needed for settings work.
When to use
- Use when locating all widgets attached to a pipeline; include inactive status because default listings omit inactive widgets.
How it works
- Reads a paginated, sortable widget list using the requested filters.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Widget rows plus pagination metadata.
Next steps
- Inspect a candidate using widgets get; for pipeline fan-out ensure inactive attachments are considered.
Usage
akedly widgets list-filtered
Inspect this command offline before preparing an operation:
akedly describe "widgets list-filtered" --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. |
--status | enum | active | inactive | suspended | Command-specific option; see the full command details below. Query parameter: status. |
--name | text | — | Command-specific option; see the full command details below. Query parameter: name. |
--pipeline-id | id | — | Command-specific option; see the full command details below. Query parameter: pipelineId. |
--has-branding | enum | true | false | Command-specific option; see the full command details below. Query parameter: hasBranding. |
--start-date | text | — | Command-specific option; see the full command details below. Query parameter: sDate. |
--end-date | text | — | Command-specific option; see the full command details below. Query parameter: eDate. |
--sort-by | enum | createdAt | name | status | updatedAt | Command-specific option; see the full command details below. Query parameter: sortBy. |
--sort-order | enum | asc | desc | Command-specific option; see the full command details below. Query parameter: sortOrder. |
--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: widgets:read.
Server request: GET /widgets/user/:userID/paginated.
Owner parameters resolved from the CLI Login: userID.
Consent
none. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
List your widgets with additional filters and owner profile fields. Read-only logins are allowed. For a returned widget, this read may disable an expired dev-mode window and delete passkeys enrolled with its Test Pair phone.
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 widgets list-filtered workflow
Find widgets by pipeline, status, name, branding or creation range, including attached widgets needed for settings work.
akedly widgets list-filtered --pipeline-id 89abcdef0123456789abcdef --status active
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"widgets": [
{
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"pipelineId": "89abcdef0123456789abcdef",
"status": "active",
"createdAt": "2030-03-12T09:00:00.000Z"
}
],
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalCount": 1,
"limit": 100,
"hasNextPage": false,
"hasPreviousPage": false
},
"appliedFilters": {
"targetUserID": null,
"status": "active",
"name": null,
"pipelineId": "89abcdef0123456789abcdef",
"hasBranding": null,
"dateRange": null
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets list-filtered
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": "status",
"schema": {
"type": "string",
"enum": [
"active",
"inactive",
"suspended"
]
},
"required": false,
"wire": [
{
"path": "status",
"transform": "identity",
"role": "query",
"input": "status",
"contributors": [
"status"
]
}
]
},
{
"name": "name",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "name",
"transform": "identity",
"role": "query",
"input": "name",
"contributors": [
"name"
]
}
]
},
{
"name": "pipeline-id",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}$"
},
"required": false,
"wire": [
{
"path": "pipelineId",
"transform": "identity",
"role": "query",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"validation": {
"syntax": "24-digit hexadecimal object ID."
}
},
{
"name": "has-branding",
"schema": {
"type": "string",
"enum": [
"true",
"false"
]
},
"required": false,
"wire": [
{
"path": "hasBranding",
"transform": "identity",
"role": "query",
"input": "has-branding",
"contributors": [
"has-branding"
]
}
]
},
{
"name": "start-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "sDate",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
]
},
{
"name": "end-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "eDate",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
]
},
{
"name": "sort-by",
"schema": {
"type": "string",
"enum": [
"createdAt",
"name",
"status",
"updatedAt"
]
},
"required": false,
"wire": [
{
"path": "sortBy",
"transform": "identity",
"role": "query",
"input": "sort-by",
"contributors": [
"sort-by"
]
}
]
},
{
"name": "sort-order",
"schema": {
"type": "string",
"enum": [
"asc",
"desc"
]
},
"required": false,
"wire": [
{
"path": "sortOrder",
"transform": "identity",
"role": "query",
"input": "sort-order",
"contributors": [
"sort-order"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/widgets/user/:userID/paginated",
"query": {
"page": [
{
"path": "page",
"transform": "identity",
"role": "query",
"input": "page",
"contributors": [
"page"
]
}
],
"limit": [
{
"path": "limit",
"transform": "identity",
"role": "query",
"input": "limit",
"contributors": [
"limit"
]
}
],
"status": [
{
"path": "status",
"transform": "identity",
"role": "query",
"input": "status",
"contributors": [
"status"
]
}
],
"name": [
{
"path": "name",
"transform": "identity",
"role": "query",
"input": "name",
"contributors": [
"name"
]
}
],
"pipeline-id": [
{
"path": "pipelineId",
"transform": "identity",
"role": "query",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"has-branding": [
{
"path": "hasBranding",
"transform": "identity",
"role": "query",
"input": "has-branding",
"contributors": [
"has-branding"
]
}
],
"start-date": [
{
"path": "sDate",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"end-date": [
{
"path": "eDate",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
],
"sort-by": [
{
"path": "sortBy",
"transform": "identity",
"role": "query",
"input": "sort-by",
"contributors": [
"sort-by"
]
}
],
"sort-order": [
{
"path": "sortOrder",
"transform": "identity",
"role": "query",
"input": "sort-order",
"contributors": [
"sort-order"
]
}
]
},
"body": {},
"ownerParams": [
"userID"
]
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"widgets": {
"type": "array"
},
"pagination": {
"type": "object"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"widgets": [
{
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"pipelineId": "89abcdef0123456789abcdef",
"status": "active",
"createdAt": "2030-03-12T09:00:00.000Z"
}
],
"pagination": {
"currentPage": 1,
"totalPages": 1,
"totalCount": 1,
"limit": 100,
"hasNextPage": false,
"hasPreviousPage": false
},
"appliedFilters": {
"targetUserID": null,
"status": "active",
"name": null,
"pipelineId": "89abcdef0123456789abcdef",
"hasBranding": null,
"dateRange": 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
}
}
widgets get
Inspect one widget’s current pipeline attachment, status, branding and security settings.
When to use
- Use before editing or diagnosing widget behavior.
How it works
- Reads a widget by its Mongo _id.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Current widget resource.
Next steps
- Use widgets update for changes; keep its current pipeline/API-version constraints in mind.
Usage
akedly widgets get <id>
Inspect this command offline before preparing an operation:
akedly describe "widgets get" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Use the 24-hex Mongo widget _id. |
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: widgets:read.
Server request: GET /widgets/: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 widget using its 24-hex Mongo _id. Dev mode returns the fake-number Test Pair; the widget Secret is never shown. Open this widget in the dashboard studio with `akedly widgets studio <id>`; opening and saving it there needs the `widgets:update` permission. For a returned widget, this read may disable an expired dev-mode window and delete passkeys enrolled with its Test Pair phone.
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 widgets get workflow
Inspect one widget’s current pipeline attachment, status, branding and security settings.
akedly widgets get 0123456789abcdef01234567
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"pipelineId": "89abcdef0123456789abcdef",
"status": "active",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets 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}$",
"validation": "Use the 24-hex Mongo widget _id.",
"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": "/widgets/:id",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"widgetId": {
"type": "string"
},
"pipelineId": {
"type": "string"
},
"status": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"pipelineId": "89abcdef0123456789abcdef",
"status": "active",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
widgets studio
Open the dashboard editor for a widget’s visual branding and customer-facing appearance.
When to use
- Use when a person needs the interactive Studio rather than a file-based settings update.
How it works
- Performs a dashboard handoff for the selected widget; no settings are changed until the user saves in the browser.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Handoff result or browser-opening instructions.
Next steps
- Finish and save in Studio, then inspect with widgets get.
Usage
akedly widgets studio <id>
Inspect this command offline before preparing an operation:
akedly describe "widgets studio" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Use the 24-hex Mongo widget _id. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--open | boolean switch | — | Command-specific option; see the full command details below. |
--help | boolean switch | — | Show offline command help. |
--output | enum | json | human | Select JSON or human output. |
--fields | dot-paths | — | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: widgets:read.
Dashboard path: /admin/widgets/:id/branding. Status check: widgets get.
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
Open an owned widget's dashboard studio, regardless of its API version or status. CLI lookup needs widgets:read; entering and saving in the browser studio needs widgets:update. This command prints a link; --open also tries to open it. It does not save branding. After the owner saves, akedly widgets get <id> shows the saved branding. The lookup is a normal widget read, so, as on any widget read, the server may disable an expired dev-mode window and delete passkeys enrolled with its Test Pair phone. Success does not mean the browser is signed in or on the same account. If you open the link in the same browser profile, on the same dashboard deployment, while its session and active account are still valid, you will usually already be signed in. A signed-out existing account is asked to sign in and returned to the studio; registration and account verification keep their existing home-page flow. Mid-session API expiry and rejected profile checks can still lose the target. With only a path available, --open is skipped with a notice; open that path on the matching dashboard. Read-only CLI logins are supported. No --yes, --dry-run, --wait or --timeout is supported. Find the widget id with akedly widgets list (add --status inactive for inactive widgets).
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 widgets studio workflow
Open the dashboard editor for a widget’s visual branding and customer-facing appearance.
akedly widgets studio 0123456789abcdef01234567
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"handoff": {
"action": "widgets studio",
"path": "/admin/widgets/0123456789abcdef01234567/branding",
"url": "https://app.example.invalid/admin/widgets/0123456789abcdef01234567/branding",
"requested": "Edit the widget's branding in the dashboard."
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets studio <id>
akedly widgets studio <id> --open
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}$",
"validation": "Use the 24-hex Mongo widget _id.",
"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,
"input": {
"role": "local-control",
"purpose": "ksa",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "ksa",
"inputRole": "local-control",
"transform": "identity",
"purpose": "ksa",
"contributors": [
"ksa"
]
}
]
},
{
"name": "open",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "open",
"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": "open",
"inputRole": "local-control",
"transform": "identity",
"purpose": "open",
"contributors": [
"open"
]
}
]
}
],
"files": [],
"wire": {
"method": null,
"route": null,
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The handoff opens the dashboard branding editor. It does not save branding; the browser session must save the changes.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"handoff": {
"type": "object",
"properties": {
"action": {
"const": "widgets studio"
},
"path": {
"type": "string"
},
"url": {
"type": "string"
},
"requested": {
"type": "string"
}
},
"required": [
"action",
"path",
"requested"
],
"additionalProperties": false
}
},
"required": [],
"additionalProperties": false
}
},
"required": [
"status",
"data"
],
"additionalProperties": false
},
"variants": [
{
"when": "No dashboard origin is configured",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"handoff": {
"type": "object",
"properties": {
"action": {
"const": "widgets studio"
},
"path": {
"type": "string"
},
"requested": {
"type": "string"
}
},
"required": [
"action",
"path",
"requested"
],
"additionalProperties": false
}
},
"required": [
"handoff"
],
"additionalProperties": false
}
},
"required": [
"status",
"data"
],
"additionalProperties": false
},
"notes": [
"The handoff has no url; the CLI prints the dashboard path for manual opening."
],
"title": "No dashboard origin is configured"
}
],
"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": {
"handoff": {
"action": "widgets studio",
"path": "/admin/widgets/0123456789abcdef01234567/branding",
"url": "https://app.example.invalid/admin/widgets/0123456789abcdef01234567/branding",
"requested": "Edit the widget's branding in the dashboard."
}
}
}
}
]
{
"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
}
}
widgets create
Create an embeddable verification widget attached to a ready pipeline.
When to use
- Use to add a customer-facing web verification surface on a ready pipeline. V1 requires the account entitlement; V2 needs both callback URLs. V1.2 pipelines cannot be attached to widgets.
How it works
- Validates optional branding/security files; may read the linked pipeline and permissions for linked settings; creates the widget, writes its one-time Secret, then applies linked pipeline settings. If the second write fails, reports PARTIAL_UPDATE and a recovery command.
Prerequisites
- A CLI login with widgets:create permission.
- An owned, active ready V1 or V2 pipeline; V1 requires widget entitlement and V2 requires frontend and backend callback URLs.
- A new private output path for the one-time widget Secret.
Expected outcome
Created widget resource with secretFile metadata, never the Secret itself; linked pipeline write can be partial.
Next steps
- Store the Secret securely and embed public widgetId/publicKey; inspect partial updates before continuing.
Usage
akedly widgets create
Inspect this command offline before preparing an operation:
akedly describe "widgets create" --output json
Flags
| Flag | Type | Enum / range | Required | Description |
|---|---|---|---|---|
--ksa | boolean switch | — | no | Select the KSA deployment. |
--name | text | — | yes | Command-specific option; see the full command details below. Manifest body field: name. |
--description | text | — | no | Command-specific option; see the full command details below. Manifest body field: description. |
--pipeline-id | id | — | no | Command-specific option; see the full command details below. Manifest body field: pipelineId. Tip: Attach widgets to a V2 pipeline. A legacy V1 pipeline is still accepted but lacks V2's security and widget features, and a V1.2 pipeline is refused. |
--secret-file | text | — | no | Write the secret to a new mode 0600 file and omit it from output; an existing path is refused. |
--dry-run | boolean switch | — | no | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--logo | text | — | no | Command-specific option; see the full command details below. Manifest body field: branding.logo, branding.customLogo. |
--company-name | text | — | no | Command-specific option; see the full command details below. Manifest body field: branding.companyName, branding.customCompanyName. |
--favicon-url | text | — | no | Command-specific option; see the full command details below. Manifest body field: branding.faviconUrl. |
--widget-language | enum | en | ar | no | Command-specific option; see the full command details below. Manifest body field: branding.appearance.language. |
--primary-color | text | — | no | Command-specific option; see the full command details below. Manifest body field: branding.appearance.theme.colors.primary, branding.customPrimaryColor. |
--secondary-color | text | — | no | Command-specific option; see the full command details below. Manifest body field: branding.appearance.theme.colors.secondary, branding.customSecondaryColor. |
--rate-limits-file | text | — | no | Command-specific option; see the full command details below. Manifest body field: rateLimiting.otpRequests.perDeviceId.perDay, rateLimiting.otpRequests.perDeviceId.perHour, rateLimiting.otpRequests.perDeviceId.perMinute, rateLimiting.otpRequests.perPhoneNumber.perDay, rateLimiting.otpRequests.perPhoneNumber.perHour, rateLimiting.otpRequests.perPhoneNumber.perMinute, rateLimiting.otpRequests.perWidget.perDay, rateLimiting.otpRequests.perWidget.perHour, rateLimiting.otpRequests.perWidget.perMinute, rateLimiting.widgetAttempts.perDeviceId.perDay, rateLimiting.widgetAttempts.perDeviceId.perHour, rateLimiting.widgetAttempts.perDeviceId.perMinute, rateLimiting.widgetAttempts.perPhoneNumber.perDay, rateLimiting.widgetAttempts.perPhoneNumber.perHour, rateLimiting.widgetAttempts.perPhoneNumber.perMinute, rateLimiting.widgetAttempts.perWidget.perDay, rateLimiting.widgetAttempts.perWidget.perHour, rateLimiting.widgetAttempts.perWidget.perMinute. Tip: Start with the default limits unless your traffic needs different ones. Use traffic recommendations when enough history is available; their calculations include a 10% growth buffer. Final widget recommendations also apply per-phone and per-device caps and may raise per-widget hour and day limits to meet the required ratios, so applied values can differ from the buffered calculation. Review the result against expected peaks before changing live limits. |
--circuit-breaker | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: circuitBreaker.enabled. Tip: Keep the circuit breaker on; Akedly recommends it to protect verification from flood attacks and traffic spikes. |
--circuit-breaker-file | text | — | no | Command-specific option; see the full command details below. Manifest body field: circuitBreaker.floodThresholds.per15Minutes, circuitBreaker.floodThresholds.per5Minutes, circuitBreaker.floodThresholds.perMinute, circuitBreaker.suspensionDurations.firstViolation, circuitBreaker.suspensionDurations.maxViolation, circuitBreaker.suspensionDurations.secondViolation. Tip: Keep the circuit breaker on; Akedly recommends it to protect verification from flood attacks and traffic spikes. |
--pow | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: security.pow.allowed. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-trigger-percent | integer | 25 to 95 | no | Command-specific option; see the full command details below. Manifest body field: security.pow.triggerPercent. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-starting-difficulty | integer | 3 to 6 | no | Command-specific option; see the full command details below. Manifest body field: security.pow.startDifficulty. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-maximum-difficulty | integer | 3 to 6 | no | Command-specific option; see the full command details below. Manifest body field: security.pow.maxDifficulty. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--appearance-file | text | — | no | Command-specific option; see the full command details below. Manifest body field: branding.appearance, branding.appearance.language, branding.appearance.theme.colors.primary, branding.customPrimaryColor, branding.appearance.theme.colors.secondary, branding.customSecondaryColor, branding.appearance.theme.mode, branding.theme. |
--yes | boolean switch | — | no | Confirm this action where consent is required. |
--smart-routing | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: smartRouting, expectedUpdatedAt. |
--passkeys | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: passkeyEnabled, expectedUpdatedAt. Tip: Passkeys let returning users verify with Face ID or a fingerprint, and a passkey sign-in can cost up to 40% less than an OTP at the default passkey rate; your account's rate may differ. |
--cluster-gate | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.enabled, clusterGateIntent, expectedUpdatedAt. |
--cluster-gate-position | enum | cautious | balanced | assertive | no | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.position, clusterGateIntent, expectedUpdatedAt. Tip: Balanced is the default position. Estimated during an attack, Cautious blocks about 65% of spam and refuses no real users, Balanced about 90% of spam and about 1% of real users, and Assertive 95% or more of spam and about 3% of real users. Assertive is outside Akedly's recommended limits: it does not check whether a request is a real user caught in the same flood. Refusals are rare outside an attack. |
--cluster-gate-escalation | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.escalation, clusterGateIntent, expectedUpdatedAt. |
--cluster-gate-home-countries | list | — | no | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.homeCountries, clusterGateIntent, expectedUpdatedAt. Tip: Declare the countries where you expect phone destinations and end-user IPs to help Cluster Gate understand your traffic. Home countries do not restrict sending or allow or block IP addresses, and other protections still apply. Learned countries stay included automatically; clearing declarations does not clear the learned set. |
--otp-language | enum | en | ar | no | Command-specific option; see the full command details below. Manifest body field: locale, expectedUpdatedAt. |
--channel-choice | enum | on | off | no | Command-specific option; see the full command details below. Manifest body field: channelChoiceEnabled, expectedUpdatedAt. |
--channel-choice-mode | enum | ask-first | send-first | no | Command-specific option; see the full command details below. Manifest body field: channelChoiceMode, expectedUpdatedAt. |
--channel-choice-enforcement | enum | fallback | hard-pin | no | Command-specific option; see the full command details below. Manifest body field: channelChoiceEnforcement, expectedUpdatedAt. |
--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: widgets:create.
Server request: POST /widgets.
Consent
--yes when --smart-routing or --passkeys or --cluster-gate or --cluster-gate-position or --cluster-gate-escalation or --cluster-gate-home-countries or --otp-language or --channel-choice or --channel-choice-mode or --channel-choice-enforcement is given. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
Output
Sensitive output: secret-sink. Use the supported sink; default output may contain a secret. Supported sinks: --secret-file.
Full command details
Create a widget with --name and --secret-file <new path>; --pipeline-id is optional. Requires Full access and widgets:create. The Secret file must not exist; it is reserved with mode 0600 before the request, filled immediately after creation succeeds, and never printed. The result includes secretFile and the stored settings. The widget Secret cannot be revealed or recovered later. After an ambiguous create failure, run akedly widgets list before intentionally creating another widget. Attach a V2 pipeline with both callback URLs, or a V1 pipeline if entitled; V1.2 cannot attach. An unattached widget cannot send. Omitted settings start with the dashboard values: widget attempts per phone and device are 2/minute, 6/hour and 10/day; OTP requests are 2/minute, 6/hour and 20/day; both per-widget limits are 25/minute, 125/hour and 2500/day. The circuit breaker starts on with flood thresholds 100/minute, 500/5 minutes and 1500/15 minutes, and suspension durations 300, 900 and 1800 seconds. Proof-of-work starts allowed with a 50% trigger, starting difficulty 3 and maximum difficulty 5. Explicit settings replace only the supplied starting values. --rate-limits-file <file> takes a JSON object with widgetAttempts and/or otpRequests, each containing perPhoneNumber, perDevice and/or perWidget, each with perMinute, perHour and/or perDay; for example {"widgetAttempts":{"perWidget":{"perMinute":30,"perHour":150,"perDay":3000}}}. Numbers must be whole and above zero. Per-phone and per-device limits cannot exceed 5/minute, 25/hour or 50/day; each per-widget hour limit must be at least 5 times its minute limit and its day limit at least 100 times its minute limit. The server checks the completed settings; inspect details.errors if it refuses them. --circuit-breaker on|off sets whether the breaker is enabled. --circuit-breaker-file <file> accepts floodThresholds with perMinute, per5Minutes and per15Minutes, and suspensionSeconds with firstViolation, secondViolation and maxViolation; supply only the values to change, as whole numbers above zero. Durations are in seconds. The file may be combined with --circuit-breaker. --pow on|off allows proof-of-work; --pow-trigger-percent takes 25..95, and --pow-starting-difficulty and --pow-maximum-difficulty take 3..6. Starting difficulty must not exceed maximum difficulty. These are stored settings; server policy still controls proof-of-work at runtime. Common branding flags are --company-name, --logo, --favicon-url, --widget-language en|ar, --primary-color and --secondary-color. --appearance-file <file> supplies the appearance object itself, without a branding or appearance wrapper; for example {"theme":{"mode":"dark"}}. The server validates its keys and values, including null, empty strings and numeric strings, and refuses unknown keys with APPEARANCE_FIELD_UNKNOWN. All settings files must be readable regular JSON object files of at most 64 KiB. Appearance keys cannot contain dots or be __proto__, prototype or constructor. Do not set the same appearance leaf through both a file and a scalar flag; disjoint values may be combined. Widget-only creation needs no --yes; changes to linked pipeline settings need --yes. Status and dev mode are update-only; captcha requirements and cooldowns cannot be changed here. Widget-only --dry-run checks inputs locally without contacting the server; linked pipeline settings perform authenticated planning reads. No dry run writes a Secret file or requires --secret-file. The server validates the final request and attachment when saving. Open this widget in the dashboard studio with `akedly widgets studio <id>`; opening and saving it there needs the `widgets:update` permission. Pipeline settings --passkeys, --smart-routing, --otp-language, --cluster-gate, --cluster-gate-position, --cluster-gate-escalation, --cluster-gate-home-countries, --channel-choice, --channel-choice-mode and --channel-choice-enforcement can be supplied here with --yes. They are written to the resulting attached pipeline after the widget is saved; an attachment change targets the new pipeline. Both write permissions are checked first. Each update carries the revision read during planning. A partial update names the saved and unconfirmed resources; after create, keep the saved Secret and use the provided pipeline update recovery command instead of creating again. --dry-run lists every planned write and saves nothing.
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 widgets create workflow
Create an embeddable verification widget attached to a ready pipeline.
akedly widgets create --name 'Example widget' --pipeline-id 89abcdef0123456789abcdef --secret-file ./private/widget-secret.txt --yes
Input file: ./fixtures/appearance.json (json).
{
"language": "en",
"theme": {
"colors": {
"primary": "#336699",
"secondary": null
}
}
}
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"publicKey": "pk_synthetic",
"secretFile": "./private/widget-secret.txt"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
The referenced pipeline must be an existing ready V1 or V2 pipeline; the synthetic ID does not assert that it exists.
Secret output sink: ./private/widget-secret.txt. Use a new private path when running the command; the synthetic example is documentation only.
Synthetic widget creation with limits, breaker, and appearance files
Create a customer-facing widget with explicit abuse limits and appearance settings on a ready V2 pipeline.
akedly widgets create --name 'Example widget' --pipeline-id 89abcdef0123456789abcdef --rate-limits-file ./fixtures/widget-rate-limits.json --circuit-breaker-file ./fixtures/widget-circuit-breaker.json --appearance-file ./fixtures/widget-appearance.json --secret-file ./private/new-widget-secret.txt --yes
Input file: ./fixtures/widget-rate-limits.json (json).
{
"otpRequests": {
"perWidget": {
"perMinute": 30
}
}
}
Input file: ./fixtures/widget-circuit-breaker.json (json).
{
"floodThresholds": {
"perMinute": 20,
"per5Minutes": 60,
"per15Minutes": 120
},
"suspensionSeconds": {
"firstViolation": 60,
"secondViolation": 300,
"maxViolation": 1800
}
}
Input file: ./fixtures/widget-appearance.json (json).
{
"language": "en",
"theme": {
"colors": {
"primary": "#336699",
"secondary": null
}
}
}
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"publicKey": "pk_synthetic",
"secretFile": "./private/new-widget-secret.txt"
}
}
Prerequisites: widgets:create permission and an owned, active, ready V2 pipeline. The synthetic pipeline ID must be replaced with an existing pipeline; V1.2 pipelines cannot be attached to widgets.
The three JSON files configure widget-level OTP-request limits, circuit-breaker thresholds and durations, and appearance. The create call also writes the widget's one-time Secret to the new private output path; keep that path separate from the input fixtures and store the real secret securely.
This response exposes secretFile metadata, not a claim that the synthetic output file exists. After creation, embed the returned widgetId/publicKey and inspect widget settings. If a linked write is partial, inspect both the widget and pipeline before continuing.
Additional command forms
akedly widgets create --name Example --pipeline-id <id> --secret-file <new path>
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "name",
"schema": {
"type": "string"
},
"required": true,
"wire": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
]
},
{
"name": "description",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "description",
"transform": "identity",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
]
},
{
"name": "pipeline-id",
"schema": {
"type": "string",
"pattern": "^[a-fA-F0-9]{24}$"
},
"required": false,
"wire": [
{
"path": "pipelineId",
"transform": "identity",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"validation": {
"syntax": "24-digit hexadecimal object ID."
}
},
{
"name": "secret-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "output-sink",
"contract": "secret-output",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"fileContract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "secret-file",
"inputRole": "output-sink",
"transform": "identity",
"fileContract": "secret-output",
"contributors": [
"secret-file"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
},
{
"name": "logo",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.logo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
},
{
"path": "branding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
}
]
},
{
"name": "company-name",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.companyName",
"transform": "none-empty",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
},
{
"path": "branding.customCompanyName",
"transform": "company-name-mirror",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
}
]
},
{
"name": "favicon-url",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.faviconUrl",
"transform": "none-empty",
"role": "body",
"input": "favicon-url",
"contributors": [
"favicon-url"
]
}
]
},
{
"name": "widget-language",
"schema": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"required": false,
"wire": [
{
"path": "branding.appearance.language",
"transform": "identity",
"role": "body",
"input": "widget-language",
"contributors": [
"widget-language"
]
}
]
},
{
"name": "primary-color",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.appearance.theme.colors.primary",
"transform": "none-empty",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
},
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
}
]
},
{
"name": "secondary-color",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.appearance.theme.colors.secondary",
"transform": "none-empty",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
},
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
}
]
},
{
"name": "rate-limits-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "input-file",
"contract": "widget-rate-limits",
"requestTargets": [
{
"role": "body",
"path": "$",
"transform": "json-fields",
"fileContract": "widget-rate-limits"
}
],
"schema": {
"$ref": "#/$defs/widgetRateLimits",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"windows": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"additionalProperties": false
},
"dimensions": {
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/windows"
},
"perDevice": {
"$ref": "#/$defs/windows"
},
"perWidget": {
"$ref": "#/$defs/windows"
}
},
"additionalProperties": false
},
"widgetRateLimits": {
"type": "object",
"description": "Each perPhoneNumber/perDevice limit is capped by the server at 5 per minute, 25 per hour and 50 per day. If perWidget perMinute is supplied, perHour must be at least 5 times it and perDay at least 100 times it when those windows are supplied; the server validates the merged settings for updates.",
"properties": {
"widgetAttempts": {
"$ref": "#/$defs/dimensions"
},
"otpRequests": {
"$ref": "#/$defs/dimensions"
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"otpRequests": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"widgetAttempts": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"otpRequests.perDevice.perDay": {
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perHour": {
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perMinute": {
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perDay": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perHour": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perMinute": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perDay": {
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perHour": {
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perMinute": {
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perDay": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perHour": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perMinute": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perDay": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perHour": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perMinute": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perDay": {
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perHour": {
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perMinute": {
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"semanticSchema": {
"$ref": "#/$defs/widgetRateLimits",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"windows": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"additionalProperties": false
},
"dimensions": {
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/windows"
},
"perDevice": {
"$ref": "#/$defs/windows"
},
"perWidget": {
"$ref": "#/$defs/windows"
}
},
"additionalProperties": false
},
"widgetRateLimits": {
"type": "object",
"description": "Each perPhoneNumber/perDevice limit is capped by the server at 5 per minute, 25 per hour and 50 per day. If perWidget perMinute is supplied, perHour must be at least 5 times it and perDay at least 100 times it when those windows are supplied; the server validates the merged settings for updates.",
"properties": {
"widgetAttempts": {
"$ref": "#/$defs/dimensions"
},
"otpRequests": {
"$ref": "#/$defs/dimensions"
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "rate-limits-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-rate-limits",
"contributors": [
"rate-limits-file"
]
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"rate-limits-file"
],
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perMinute",
"inputTransform": "json-fields"
}
]
},
{
"name": "circuit-breaker",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"wire": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "circuit-breaker-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "input-file",
"contract": "widget-circuit-breaker",
"requestTargets": [
{
"role": "body",
"path": "$",
"transform": "json-fields",
"fileContract": "widget-circuit-breaker"
}
],
"schema": {
"$ref": "#/$defs/widgetBreaker",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"widgetBreaker": {
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"per15Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"per5Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"maxViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"secondViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"floodThresholds.per15Minutes": {
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.per5Minutes": {
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.perMinute": {
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"suspensionSeconds.firstViolation": {
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.maxViolation": {
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.secondViolation": {
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"semanticSchema": {
"$ref": "#/$defs/widgetBreaker",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"widgetBreaker": {
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "circuit-breaker-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"contributors": [
"circuit-breaker-file"
]
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker-file"
],
"valueType": "positive-integer",
"fileKey": "floodThresholds.per15Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker-file"
],
"valueType": "positive-integer",
"fileKey": "floodThresholds.per5Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker-file"
],
"valueType": "positive-integer",
"fileKey": "floodThresholds.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker-file"
],
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.firstViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker-file"
],
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.maxViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker-file"
],
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.secondViolation",
"inputTransform": "json-fields"
}
]
},
{
"name": "pow",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"wire": [
{
"path": "security.pow.allowed",
"transform": "identity",
"role": "body",
"input": "pow",
"contributors": [
"pow"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "pow-trigger-percent",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:(?:2[5-9]|[3-8]\\d{1}|9[0-5]))(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "security.pow.triggerPercent",
"transform": "identity",
"role": "body",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 25,
"maximum": 95,
"parsedValueSchema": {
"type": "integer",
"minimum": 25,
"maximum": 95
}
}
},
{
"name": "pow-starting-difficulty",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[3-6])(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "security.pow.startDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 3,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 3,
"maximum": 6
}
}
},
{
"name": "pow-maximum-difficulty",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[3-6])(?:\\.0+)?)$"
},
"required": false,
"wire": [
{
"path": "security.pow.maxDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 3,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 3,
"maximum": 6
}
}
},
{
"name": "appearance-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "input-file",
"contract": "widget-appearance",
"requestTargets": [
{
"role": "body",
"path": "branding.appearance",
"transform": "json-object",
"fileContract": "widget-appearance"
}
],
"schema": {
"$ref": "#/$defs/appearance",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"hex": {
"anyOf": [
{
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"enumOrUnset": {
"type": [
"string",
"null"
]
},
"numOrUnset": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"appearance": {
"type": "object",
"description": "Known appearance keys and value checks are modeled below. The CLI forwards the appearance object; unknown keys are reported as APPEARANCE_FIELD_UNKNOWN and the backend's dashboard-rule policy determines whether they are refused or allowed in report mode. Keys with dots or __proto__/prototype/constructor are rejected locally.",
"properties": {
"language": {
"type": [
"string",
"null"
],
"enum": [
"en",
"ar",
null,
""
]
},
"preset": {
"type": [
"string",
"null"
],
"maxLength": 120
},
"theme": {
"$ref": "#/$defs/theme",
"type": [
"object",
"null"
],
"description": "Appearance theme; null replaces the saved theme section, after which appearance normalization supplies its defaults."
},
"layout": {
"$ref": "#/$defs/layout",
"type": [
"object",
"null"
],
"description": "Appearance layout; null replaces the saved layout section, after which appearance normalization supplies its defaults."
},
"components": {
"$ref": "#/$defs/components",
"type": [
"object",
"null"
],
"description": "Appearance components; null replaces the saved components section, after which appearance normalization supplies its defaults."
},
"copy": {
"$ref": "#/$defs/copy",
"type": [
"object",
"null"
],
"description": "Appearance copy; null replaces the saved copy section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"theme": {
"type": [
"object",
"null"
],
"properties": {
"mode": {
"type": [
"string",
"null"
],
"enum": [
"light",
"dark",
"system",
null,
""
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"density": {
"type": [
"string",
"null"
],
"enum": [
"compact",
"comfortable",
"spacious",
null,
""
]
},
"densityScale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0.6,
"maximum": 1.6
},
"colors": {
"type": [
"object",
"null"
],
"properties": {
"primary": {
"$ref": "#/$defs/hex"
},
"secondary": {
"$ref": "#/$defs/hex"
},
"accent": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
},
"foreground": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Theme colors; null replaces the saved colors section, after which appearance normalization supplies its defaults."
},
"font": {
"type": [
"object",
"null"
],
"properties": {
"family": {
"type": [
"string",
"null"
],
"enum": [
"system",
"inter",
"roboto",
"open-sans",
"lato",
"montserrat",
"poppins",
"nunito",
"nunito-sans",
"work-sans",
"dm-sans",
"sora",
"space-grotesk",
"manrope",
"rubik",
"mulish",
"karla",
"plus-jakarta-sans",
"figtree",
"outfit",
"albert-sans",
"public-sans",
"ibm-plex-sans",
"source-sans-3",
"pt-sans",
"raleway",
"quicksand",
"josefin-sans",
"barlow",
"cabin",
"heebo",
"assistant",
"red-hat-display",
"lexend",
"onest",
"geist",
"schibsted-grotesk",
"instrument-sans",
"be-vietnam-pro",
"merriweather",
"lora",
"playfair-display",
"source-serif-4",
"pt-serif",
"bitter",
"jetbrains-mono",
"ibm-plex-mono",
"space-mono",
"cairo",
"tajawal",
"ibm-plex-sans-arabic",
"noto-sans-arabic",
null
]
},
"baseSize": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"headingWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 400,
"maximum": 900
},
"bodyWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 700
}
},
"additionalProperties": true,
"description": "Theme font; null replaces the saved font section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"layout": {
"type": [
"object",
"null"
],
"properties": {
"template": {
"type": [
"string",
"null"
],
"enum": [
"centered",
"split-left",
"split-right",
"compact",
null,
""
]
},
"logoPosition": {
"type": [
"string",
"null"
],
"enum": [
"top",
"bottom",
"inline",
null,
""
]
},
"align": {
"type": [
"string",
"null"
],
"enum": [
"center",
"left",
null,
""
]
},
"panel": {
"type": [
"object",
"null"
],
"properties": {
"background": {
"$ref": "#/$defs/hex"
},
"imageUrl": {
"anyOf": [
{
"type": "string",
"format": "uri",
"pattern": "^https://"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"tagline": {
"type": [
"string",
"null"
],
"maxLength": 120
}
},
"additionalProperties": true,
"description": "Layout panel; null replaces the saved panel section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"components": {
"type": [
"object",
"null"
],
"properties": {
"button": {
"type": [
"object",
"null"
],
"properties": {
"variant": {
"type": [
"string",
"null"
],
"enum": [
"solid",
"outline",
"soft",
null,
""
]
},
"size": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"textColor": {
"$ref": "#/$defs/hex"
},
"fullWidth": {
"type": [
"boolean",
"null"
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"scale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 1,
"maximum": 1.08
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "Button component settings; null replaces that section and appearance normalization supplies defaults."
},
"otp": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"gap": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 24
},
"activeColor": {
"$ref": "#/$defs/hex"
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "OTP component settings; null replaces that section and appearance normalization supplies defaults."
},
"card": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"border": {
"type": [
"boolean",
"null"
]
},
"ring": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Card component settings; null replaces that section and appearance normalization supplies defaults."
},
"link": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"weight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 800
},
"underline": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Link component settings; null replaces that section and appearance normalization supplies defaults."
},
"success": {
"type": [
"object",
"null"
],
"properties": {
"confetti": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Success component settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
},
"copy": {
"type": [
"object",
"null"
],
"properties": {
"default": {
"type": [
"object",
"null"
],
"properties": {
"title": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"subtitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"ctaLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"resendLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successTitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"errorMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
}
},
"additionalProperties": true,
"description": "Default copy map; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"JsonValue": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"$ref": "#/$defs/JsonObject"
},
{
"$ref": "#/$defs/JsonArray"
}
]
},
"JsonObject": {
"type": "object",
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
},
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
}
},
"JsonArray": {
"type": "array",
"items": {
"$ref": "#/$defs/JsonValue"
}
}
},
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
}
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"language": {
"path": "branding.appearance.language",
"transform": "identity"
},
"theme.colors.primary": {
"path": "branding.appearance.theme.colors.primary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror"
}
]
},
"theme.colors.secondary": {
"path": "branding.appearance.theme.colors.secondary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror"
}
]
},
"theme.mode": {
"path": "branding.appearance.theme.mode",
"transform": "identity",
"mirrors": [
{
"path": "branding.theme",
"transform": "identity"
}
]
}
},
"settings": true,
"resource": "widget",
"objectPath": "branding.appearance",
"serverValidated": true
},
"semanticSchema": {
"$ref": "#/$defs/appearance",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"hex": {
"anyOf": [
{
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"enumOrUnset": {
"type": [
"string",
"null"
]
},
"numOrUnset": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"appearance": {
"type": "object",
"description": "Known appearance keys and value checks are modeled below. The CLI forwards the appearance object; unknown keys are reported as APPEARANCE_FIELD_UNKNOWN and the backend's dashboard-rule policy determines whether they are refused or allowed in report mode. Keys with dots or __proto__/prototype/constructor are rejected locally.",
"properties": {
"language": {
"type": [
"string",
"null"
],
"enum": [
"en",
"ar",
null,
""
]
},
"preset": {
"type": [
"string",
"null"
],
"maxLength": 120
},
"theme": {
"$ref": "#/$defs/theme",
"type": [
"object",
"null"
],
"description": "Appearance theme; null replaces the saved theme section, after which appearance normalization supplies its defaults."
},
"layout": {
"$ref": "#/$defs/layout",
"type": [
"object",
"null"
],
"description": "Appearance layout; null replaces the saved layout section, after which appearance normalization supplies its defaults."
},
"components": {
"$ref": "#/$defs/components",
"type": [
"object",
"null"
],
"description": "Appearance components; null replaces the saved components section, after which appearance normalization supplies its defaults."
},
"copy": {
"$ref": "#/$defs/copy",
"type": [
"object",
"null"
],
"description": "Appearance copy; null replaces the saved copy section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"theme": {
"type": [
"object",
"null"
],
"properties": {
"mode": {
"type": [
"string",
"null"
],
"enum": [
"light",
"dark",
"system",
null,
""
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"density": {
"type": [
"string",
"null"
],
"enum": [
"compact",
"comfortable",
"spacious",
null,
""
]
},
"densityScale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0.6,
"maximum": 1.6
},
"colors": {
"type": [
"object",
"null"
],
"properties": {
"primary": {
"$ref": "#/$defs/hex"
},
"secondary": {
"$ref": "#/$defs/hex"
},
"accent": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
},
"foreground": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Theme colors; null replaces the saved colors section, after which appearance normalization supplies its defaults."
},
"font": {
"type": [
"object",
"null"
],
"properties": {
"family": {
"type": [
"string",
"null"
],
"enum": [
"system",
"inter",
"roboto",
"open-sans",
"lato",
"montserrat",
"poppins",
"nunito",
"nunito-sans",
"work-sans",
"dm-sans",
"sora",
"space-grotesk",
"manrope",
"rubik",
"mulish",
"karla",
"plus-jakarta-sans",
"figtree",
"outfit",
"albert-sans",
"public-sans",
"ibm-plex-sans",
"source-sans-3",
"pt-sans",
"raleway",
"quicksand",
"josefin-sans",
"barlow",
"cabin",
"heebo",
"assistant",
"red-hat-display",
"lexend",
"onest",
"geist",
"schibsted-grotesk",
"instrument-sans",
"be-vietnam-pro",
"merriweather",
"lora",
"playfair-display",
"source-serif-4",
"pt-serif",
"bitter",
"jetbrains-mono",
"ibm-plex-mono",
"space-mono",
"cairo",
"tajawal",
"ibm-plex-sans-arabic",
"noto-sans-arabic",
null
]
},
"baseSize": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"headingWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 400,
"maximum": 900
},
"bodyWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 700
}
},
"additionalProperties": true,
"description": "Theme font; null replaces the saved font section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"layout": {
"type": [
"object",
"null"
],
"properties": {
"template": {
"type": [
"string",
"null"
],
"enum": [
"centered",
"split-left",
"split-right",
"compact",
null,
""
]
},
"logoPosition": {
"type": [
"string",
"null"
],
"enum": [
"top",
"bottom",
"inline",
null,
""
]
},
"align": {
"type": [
"string",
"null"
],
"enum": [
"center",
"left",
null,
""
]
},
"panel": {
"type": [
"object",
"null"
],
"properties": {
"background": {
"$ref": "#/$defs/hex"
},
"imageUrl": {
"anyOf": [
{
"type": "string",
"format": "uri",
"pattern": "^https://"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"tagline": {
"type": [
"string",
"null"
],
"maxLength": 120
}
},
"additionalProperties": true,
"description": "Layout panel; null replaces the saved panel section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"components": {
"type": [
"object",
"null"
],
"properties": {
"button": {
"type": [
"object",
"null"
],
"properties": {
"variant": {
"type": [
"string",
"null"
],
"enum": [
"solid",
"outline",
"soft",
null,
""
]
},
"size": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"textColor": {
"$ref": "#/$defs/hex"
},
"fullWidth": {
"type": [
"boolean",
"null"
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"scale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 1,
"maximum": 1.08
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "Button component settings; null replaces that section and appearance normalization supplies defaults."
},
"otp": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"gap": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 24
},
"activeColor": {
"$ref": "#/$defs/hex"
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "OTP component settings; null replaces that section and appearance normalization supplies defaults."
},
"card": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"border": {
"type": [
"boolean",
"null"
]
},
"ring": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Card component settings; null replaces that section and appearance normalization supplies defaults."
},
"link": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"weight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 800
},
"underline": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Link component settings; null replaces that section and appearance normalization supplies defaults."
},
"success": {
"type": [
"object",
"null"
],
"properties": {
"confetti": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Success component settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
},
"copy": {
"type": [
"object",
"null"
],
"properties": {
"default": {
"type": [
"object",
"null"
],
"properties": {
"title": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"subtitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"ctaLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"resendLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successTitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"errorMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
}
},
"additionalProperties": true,
"description": "Default copy map; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
}
}
},
"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": "appearance-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-appearance",
"contributors": [
"appearance-file"
]
},
{
"role": "body",
"path": "branding.appearance",
"transform": "json-object",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
]
},
{
"role": "body",
"path": "branding.appearance.language",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "language",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.appearance.theme.colors.primary",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.primary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.primary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.appearance.theme.colors.secondary",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.secondary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.secondary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.appearance.theme.mode",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.mode",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.theme",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.mode",
"inputTransform": "json-fields"
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "confirmation",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "confirmation",
"contributors": [
"yes"
]
}
]
},
{
"name": "smart-routing",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "smartRouting",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "passkeys",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.enabled",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate-position",
"schema": {
"type": "string",
"enum": [
"cautious",
"balanced",
"assertive"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.position",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
]
},
{
"name": "cluster-gate-escalation",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.escalation",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate-home-countries",
"schema": {
"type": "string",
"pattern": "^(?:[^,]*[^,\\s][^,]*)(?:,(?:[^,]*[^,\\s][^,]*))*$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"validation": {
"syntax": "Comma-separated values; every segment must contain at least one non-whitespace character."
}
},
{
"name": "otp-language",
"schema": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "locale",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
]
},
{
"name": "channel-choice",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "channel-choice-mode",
"schema": {
"type": "string",
"enum": [
"ask-first",
"send-first"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceMode",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"valueTranslations": {
"ask-first": "ask_first",
"send-first": "send_first"
}
},
{
"name": "channel-choice-enforcement",
"schema": {
"type": "string",
"enum": [
"fallback",
"hard-pin"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
],
"valueTranslations": {
"fallback": "fallback",
"hard-pin": "hard_pin"
}
}
],
"files": [
{
"name": "secret-output",
"contract": {
"format": "secret",
"exclusive": true,
"mode": "0600",
"writer": "secrets.js:openSecretFile",
"type": "secret",
"regularFile": false,
"trim": false,
"reader": null,
"maxBytes": null,
"minBytes": 0,
"detectChanges": false,
"fieldMappings": {}
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string"
},
"uses": [
{
"flag": "secret-file",
"role": "output-sink",
"purpose": null
}
]
},
{
"name": "widget-rate-limits",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"otpRequests.perDevice.perDay": {
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perHour": {
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perMinute": {
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perDay": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perHour": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perMinute": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perDay": {
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perHour": {
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perMinute": {
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perDay": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perHour": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perMinute": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perDay": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perHour": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perMinute": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perDay": {
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perHour": {
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perMinute": {
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"otpRequests": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"widgetAttempts": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"uses": [
{
"flag": "rate-limits-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$ref": "#/$defs/widgetRateLimits",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"windows": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"additionalProperties": false
},
"dimensions": {
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/windows"
},
"perDevice": {
"$ref": "#/$defs/windows"
},
"perWidget": {
"$ref": "#/$defs/windows"
}
},
"additionalProperties": false
},
"widgetRateLimits": {
"type": "object",
"description": "Each perPhoneNumber/perDevice limit is capped by the server at 5 per minute, 25 per hour and 50 per day. If perWidget perMinute is supplied, perHour must be at least 5 times it and perDay at least 100 times it when those windows are supplied; the server validates the merged settings for updates.",
"properties": {
"widgetAttempts": {
"$ref": "#/$defs/dimensions"
},
"otpRequests": {
"$ref": "#/$defs/dimensions"
}
},
"minProperties": 1,
"additionalProperties": false
}
}
}
}
]
},
{
"name": "widget-circuit-breaker",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"floodThresholds.per15Minutes": {
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.per5Minutes": {
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.perMinute": {
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"suspensionSeconds.firstViolation": {
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.maxViolation": {
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.secondViolation": {
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"per15Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"per5Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"maxViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"secondViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"uses": [
{
"flag": "circuit-breaker-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$ref": "#/$defs/widgetBreaker",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"widgetBreaker": {
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false
}
}
}
}
]
},
{
"name": "widget-appearance",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"language": {
"path": "branding.appearance.language",
"transform": "identity"
},
"theme.colors.primary": {
"path": "branding.appearance.theme.colors.primary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror"
}
]
},
"theme.colors.secondary": {
"path": "branding.appearance.theme.colors.secondary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror"
}
]
},
"theme.mode": {
"path": "branding.appearance.theme.mode",
"transform": "identity",
"mirrors": [
{
"path": "branding.theme",
"transform": "identity"
}
]
}
},
"settings": true,
"resource": "widget",
"objectPath": "branding.appearance",
"serverValidated": true
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"JsonValue": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"$ref": "#/$defs/JsonObject"
},
{
"$ref": "#/$defs/JsonArray"
}
]
},
"JsonObject": {
"type": "object",
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
},
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
}
},
"JsonArray": {
"type": "array",
"items": {
"$ref": "#/$defs/JsonValue"
}
}
},
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
}
},
"uses": [
{
"flag": "appearance-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$ref": "#/$defs/appearance",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"hex": {
"anyOf": [
{
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"enumOrUnset": {
"type": [
"string",
"null"
]
},
"numOrUnset": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"appearance": {
"type": "object",
"description": "Known appearance keys and value checks are modeled below. The CLI forwards the appearance object; unknown keys are reported as APPEARANCE_FIELD_UNKNOWN and the backend's dashboard-rule policy determines whether they are refused or allowed in report mode. Keys with dots or __proto__/prototype/constructor are rejected locally.",
"properties": {
"language": {
"type": [
"string",
"null"
],
"enum": [
"en",
"ar",
null,
""
]
},
"preset": {
"type": [
"string",
"null"
],
"maxLength": 120
},
"theme": {
"$ref": "#/$defs/theme",
"type": [
"object",
"null"
],
"description": "Appearance theme; null replaces the saved theme section, after which appearance normalization supplies its defaults."
},
"layout": {
"$ref": "#/$defs/layout",
"type": [
"object",
"null"
],
"description": "Appearance layout; null replaces the saved layout section, after which appearance normalization supplies its defaults."
},
"components": {
"$ref": "#/$defs/components",
"type": [
"object",
"null"
],
"description": "Appearance components; null replaces the saved components section, after which appearance normalization supplies its defaults."
},
"copy": {
"$ref": "#/$defs/copy",
"type": [
"object",
"null"
],
"description": "Appearance copy; null replaces the saved copy section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"theme": {
"type": [
"object",
"null"
],
"properties": {
"mode": {
"type": [
"string",
"null"
],
"enum": [
"light",
"dark",
"system",
null,
""
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"density": {
"type": [
"string",
"null"
],
"enum": [
"compact",
"comfortable",
"spacious",
null,
""
]
},
"densityScale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0.6,
"maximum": 1.6
},
"colors": {
"type": [
"object",
"null"
],
"properties": {
"primary": {
"$ref": "#/$defs/hex"
},
"secondary": {
"$ref": "#/$defs/hex"
},
"accent": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
},
"foreground": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Theme colors; null replaces the saved colors section, after which appearance normalization supplies its defaults."
},
"font": {
"type": [
"object",
"null"
],
"properties": {
"family": {
"type": [
"string",
"null"
],
"enum": [
"system",
"inter",
"roboto",
"open-sans",
"lato",
"montserrat",
"poppins",
"nunito",
"nunito-sans",
"work-sans",
"dm-sans",
"sora",
"space-grotesk",
"manrope",
"rubik",
"mulish",
"karla",
"plus-jakarta-sans",
"figtree",
"outfit",
"albert-sans",
"public-sans",
"ibm-plex-sans",
"source-sans-3",
"pt-sans",
"raleway",
"quicksand",
"josefin-sans",
"barlow",
"cabin",
"heebo",
"assistant",
"red-hat-display",
"lexend",
"onest",
"geist",
"schibsted-grotesk",
"instrument-sans",
"be-vietnam-pro",
"merriweather",
"lora",
"playfair-display",
"source-serif-4",
"pt-serif",
"bitter",
"jetbrains-mono",
"ibm-plex-mono",
"space-mono",
"cairo",
"tajawal",
"ibm-plex-sans-arabic",
"noto-sans-arabic",
null
]
},
"baseSize": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"headingWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 400,
"maximum": 900
},
"bodyWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 700
}
},
"additionalProperties": true,
"description": "Theme font; null replaces the saved font section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"layout": {
"type": [
"object",
"null"
],
"properties": {
"template": {
"type": [
"string",
"null"
],
"enum": [
"centered",
"split-left",
"split-right",
"compact",
null,
""
]
},
"logoPosition": {
"type": [
"string",
"null"
],
"enum": [
"top",
"bottom",
"inline",
null,
""
]
},
"align": {
"type": [
"string",
"null"
],
"enum": [
"center",
"left",
null,
""
]
},
"panel": {
"type": [
"object",
"null"
],
"properties": {
"background": {
"$ref": "#/$defs/hex"
},
"imageUrl": {
"anyOf": [
{
"type": "string",
"format": "uri",
"pattern": "^https://"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"tagline": {
"type": [
"string",
"null"
],
"maxLength": 120
}
},
"additionalProperties": true,
"description": "Layout panel; null replaces the saved panel section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"components": {
"type": [
"object",
"null"
],
"properties": {
"button": {
"type": [
"object",
"null"
],
"properties": {
"variant": {
"type": [
"string",
"null"
],
"enum": [
"solid",
"outline",
"soft",
null,
""
]
},
"size": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"textColor": {
"$ref": "#/$defs/hex"
},
"fullWidth": {
"type": [
"boolean",
"null"
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"scale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 1,
"maximum": 1.08
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "Button component settings; null replaces that section and appearance normalization supplies defaults."
},
"otp": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"gap": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 24
},
"activeColor": {
"$ref": "#/$defs/hex"
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "OTP component settings; null replaces that section and appearance normalization supplies defaults."
},
"card": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"border": {
"type": [
"boolean",
"null"
]
},
"ring": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Card component settings; null replaces that section and appearance normalization supplies defaults."
},
"link": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"weight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 800
},
"underline": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Link component settings; null replaces that section and appearance normalization supplies defaults."
},
"success": {
"type": [
"object",
"null"
],
"properties": {
"confetti": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Success component settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
},
"copy": {
"type": [
"object",
"null"
],
"properties": {
"default": {
"type": [
"object",
"null"
],
"properties": {
"title": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"subtitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"ctaLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"resendLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successTitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"errorMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
}
},
"additionalProperties": true,
"description": "Default copy map; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
}
}
}
}
]
}
],
"wire": {
"method": "POST",
"route": "/widgets",
"query": {},
"body": {
"name": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
],
"description": [
{
"path": "description",
"transform": "identity",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
],
"pipeline-id": [
{
"path": "pipelineId",
"transform": "identity",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"logo": [
{
"path": "branding.logo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
},
{
"path": "branding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
}
],
"company-name": [
{
"path": "branding.companyName",
"transform": "none-empty",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
},
{
"path": "branding.customCompanyName",
"transform": "company-name-mirror",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
}
],
"favicon-url": [
{
"path": "branding.faviconUrl",
"transform": "none-empty",
"role": "body",
"input": "favicon-url",
"contributors": [
"favicon-url"
]
}
],
"widget-language": [
{
"path": "branding.appearance.language",
"transform": "identity",
"role": "body",
"input": "widget-language",
"contributors": [
"widget-language"
]
}
],
"primary-color": [
{
"path": "branding.appearance.theme.colors.primary",
"transform": "none-empty",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
},
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
}
],
"secondary-color": [
{
"path": "branding.appearance.theme.colors.secondary",
"transform": "none-empty",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
},
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
}
],
"circuit-breaker": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker"
]
}
],
"pow": [
{
"path": "security.pow.allowed",
"transform": "identity",
"role": "body",
"input": "pow",
"contributors": [
"pow"
]
}
],
"pow-trigger-percent": [
{
"path": "security.pow.triggerPercent",
"transform": "identity",
"role": "body",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
}
],
"pow-starting-difficulty": [
{
"path": "security.pow.startDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
],
"pow-maximum-difficulty": [
{
"path": "security.pow.maxDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
}
],
"smart-routing": [
{
"path": "smartRouting",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"passkeys": [
{
"path": "passkeyEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"cluster-gate": [
{
"path": "securityConfig.clusterGate.enabled",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-position": [
{
"path": "securityConfig.clusterGate.position",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-escalation": [
{
"path": "securityConfig.clusterGate.escalation",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-home-countries": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"otp-language": [
{
"path": "locale",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
],
"channel-choice": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"channel-choice-mode": [
{
"path": "channelChoiceMode",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"channel-choice-enforcement": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
Secret bytes are delivered only to the requested local file; the JSON response contains file metadata, never the secret value.
Protected linked-resource writes can be partial; honor PARTIAL_UPDATE details and inspect each named resource before retrying.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"widgetId": {
"type": "string"
},
"publicKey": {
"type": "string"
},
"secretFile": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--secret-file receives one-time secret delivery",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"widgetId": {
"type": "string"
},
"publicKey": {
"type": "string"
},
"secretFile": {
"type": "string"
}
},
"required": [
"_id",
"name",
"widgetId",
"publicKey",
"secretFile"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"The response identifies secretFile; secret material is written to that private file."
],
"title": "--secret-file receives one-time secret delivery"
},
{
"when": "A later linked write cannot be confirmed",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"code": {
"const": "PARTIAL_UPDATE"
},
"message": {
"type": "string"
},
"details": {
"type": "object",
"properties": {
"applied": {
"type": "array",
"items": {
"type": "object",
"properties": {
"resource": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [
"resource",
"id"
],
"additionalProperties": true
}
},
"notApplied": {
"type": "array",
"items": {
"type": "object",
"properties": {
"resource": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [
"resource",
"id"
],
"additionalProperties": true
}
},
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
}
},
"required": [
"code",
"httpStatus"
],
"additionalProperties": true
}
},
"required": [
"applied",
"notApplied",
"error"
],
"additionalProperties": true
},
"hint": {
"type": "string"
}
},
"required": [
"code",
"message",
"details",
"hint"
],
"additionalProperties": true
},
"notes": [
"Inspect applied and notApplied resources. A rerun may repeat a write that completed without confirmation."
],
"title": "A later linked write cannot be confirmed"
},
{
"when": "--dry-run when linked settings require multiple resource writes",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
}
}
},
"required": [
"steps"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"Linked planning returns status:\"success\" and data.steps. Planning reads may occur; none of the listed writes is executed."
],
"title": "--dry-run when linked settings require multiple resource writes"
},
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1/widgets",
"body": {
"name": "Example widget",
"pipelineId": "89abcdef0123456789abcdef"
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": "Projection is not redaction; selecting sensitive paths can print Secrets or live OTP values. Use the command secret sink where supported and keep output private."
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"publicKey": "pk_synthetic",
"secretFile": "./private/widget-secret.txt"
}
}
}
]
{
"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": {
"WIDGET_CHANGED": "Run akedly widgets get <id> to inspect the current settings, then rerun the intended update. The command reads a fresh revision; do not reuse an old request body to overwrite another change.",
"WIDGET_POW_INVALID": "Use --pow on|off, --pow-trigger-percent 25..95, and --pow-starting-difficulty and --pow-maximum-difficulty 3..6. Starting difficulty must not exceed maximum difficulty. If changing only one difficulty, inspect the other with akedly widgets get <id>; an unset start is compared as 3 and an unset maximum as 5.",
"APPEARANCE_FIELD_UNKNOWN": "Remove or correct the appearance key named in details.field in --appearance-file. Supply the appearance object itself without a branding or appearance wrapper; the server decides which keys and values are supported. Unknown keys are refused rather than dropped."
},
"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 |
|---|---|
| WIDGET_CHANGED | Run akedly widgets get <id> to inspect the current settings, then rerun the intended update. The command reads a fresh revision; do not reuse an old request body to overwrite another change. |
| WIDGET_POW_INVALID | Use --pow on|off, --pow-trigger-percent 25..95, and --pow-starting-difficulty and --pow-maximum-difficulty 3..6. Starting difficulty must not exceed maximum difficulty. If changing only one difficulty, inspect the other with akedly widgets get <id>; an unset start is compared as 3 and an unset maximum as 5. |
| APPEARANCE_FIELD_UNKNOWN | Remove or correct the appearance key named in details.field in --appearance-file. Supply the appearance object itself without a branding or appearance wrapper; the server decides which keys and values are supported. Unknown keys are refused rather than dropped. |
widgets update
Change widget identity, appearance, rate limits, circuit breaker, PoW or linked pipeline settings.
When to use
- Use after widgets get; supply the widget _id and only the settings intended for that widget/pipeline.
How it works
- Reads current widget/pipeline revisions as required; dry-run performs planning reads and previews writes. Linked pipeline settings are saved after widget settings, so PARTIAL_UPDATE is possible.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Updated widget resource or multi-step dry-run plan; linked writes can be partial.
Next steps
- On PARTIAL_UPDATE inspect both widget and pipeline before retrying; revisions are read automatically.
Usage
akedly widgets update <id>
Inspect this command offline before preparing an operation:
akedly describe "widgets update" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Use the 24-hex Mongo widget _id. |
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. |
--pipeline-id | id | — | Command-specific option; see the full command details below. Manifest body field: pipelineId. The literal none clears this value. Tip: Attach widgets to a V2 pipeline. A legacy V1 pipeline is still accepted but lacks V2's security and widget features, and a V1.2 pipeline is refused. |
--status | enum | active | inactive | Command-specific option; see the full command details below. Manifest body field: status. |
--logo | text | — | Command-specific option; see the full command details below. Manifest body field: branding.logo, branding.customLogo. |
--company-name | text | — | Command-specific option; see the full command details below. Manifest body field: branding.companyName, branding.customCompanyName. |
--favicon-url | text | — | Command-specific option; see the full command details below. Manifest body field: branding.faviconUrl. |
--widget-language | enum | en | ar | Command-specific option; see the full command details below. Manifest body field: branding.appearance.language. |
--primary-color | text | — | Command-specific option; see the full command details below. Manifest body field: branding.appearance.theme.colors.primary, branding.customPrimaryColor. |
--secondary-color | text | — | Command-specific option; see the full command details below. Manifest body field: branding.appearance.theme.colors.secondary, branding.customSecondaryColor. |
--dev-mode | enum | on | off | Command-specific option; see the full command details below. Manifest body field: devMode.enabled. Tip: Do not leave dev mode on for production traffic: while the window is open, anyone who knows the test number and code can complete a verification for that number. |
--dev-mode-digits | integer | 4 to 6 | Command-specific option; see the full command details below. Manifest body field: devMode.digits. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--rate-limits-file | text | — | Command-specific option; see the full command details below. Manifest body field: rateLimiting.otpRequests.perDeviceId.perDay, rateLimiting.otpRequests.perDeviceId.perHour, rateLimiting.otpRequests.perDeviceId.perMinute, rateLimiting.otpRequests.perPhoneNumber.perDay, rateLimiting.otpRequests.perPhoneNumber.perHour, rateLimiting.otpRequests.perPhoneNumber.perMinute, rateLimiting.otpRequests.perWidget.perDay, rateLimiting.otpRequests.perWidget.perHour, rateLimiting.otpRequests.perWidget.perMinute, rateLimiting.widgetAttempts.perDeviceId.perDay, rateLimiting.widgetAttempts.perDeviceId.perHour, rateLimiting.widgetAttempts.perDeviceId.perMinute, rateLimiting.widgetAttempts.perPhoneNumber.perDay, rateLimiting.widgetAttempts.perPhoneNumber.perHour, rateLimiting.widgetAttempts.perPhoneNumber.perMinute, rateLimiting.widgetAttempts.perWidget.perDay, rateLimiting.widgetAttempts.perWidget.perHour, rateLimiting.widgetAttempts.perWidget.perMinute, expectedUpdatedAt. Tip: Start with the default limits unless your traffic needs different ones. Use traffic recommendations when enough history is available; their calculations include a 10% growth buffer. Final widget recommendations also apply per-phone and per-device caps and may raise per-widget hour and day limits to meet the required ratios, so applied values can differ from the buffered calculation. Review the result against expected peaks before changing live limits. |
--circuit-breaker | enum | on | off | Command-specific option; see the full command details below. Manifest body field: circuitBreaker.enabled, expectedUpdatedAt. Tip: Keep the circuit breaker on; Akedly recommends it to protect verification from flood attacks and traffic spikes. |
--circuit-breaker-file | text | — | Command-specific option; see the full command details below. Manifest body field: circuitBreaker.floodThresholds.per15Minutes, circuitBreaker.floodThresholds.per5Minutes, circuitBreaker.floodThresholds.perMinute, circuitBreaker.suspensionDurations.firstViolation, circuitBreaker.suspensionDurations.maxViolation, circuitBreaker.suspensionDurations.secondViolation, expectedUpdatedAt. Tip: Keep the circuit breaker on; Akedly recommends it to protect verification from flood attacks and traffic spikes. |
--pow | enum | on | off | Command-specific option; see the full command details below. Manifest body field: security.pow.allowed. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-trigger-percent | integer | 25 to 95 | Command-specific option; see the full command details below. Manifest body field: security.pow.triggerPercent. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-starting-difficulty | integer | 3 to 6 | Command-specific option; see the full command details below. Manifest body field: security.pow.startDifficulty. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--pow-maximum-difficulty | integer | 3 to 6 | Command-specific option; see the full command details below. Manifest body field: security.pow.maxDifficulty. Tip: Keep the default proof-of-work settings unless you face sustained heavy abuse. For an expected launch or campaign surge, raise the rate limits and circuit breaker flood thresholds first instead of lowering the proof-of-work trigger, so legitimate traffic is not challenged too early. |
--appearance-file | text | — | Command-specific option; see the full command details below. Manifest body field: branding.appearance, branding.appearance.language, branding.appearance.theme.colors.primary, branding.customPrimaryColor, branding.appearance.theme.colors.secondary, branding.customSecondaryColor, branding.appearance.theme.mode, branding.theme. |
--recommended-limits | boolean switch | — | Command-specific option; see the full command details below. Manifest body field: rateLimiting.otpRequests.perDeviceId.perDay, rateLimiting.otpRequests.perDeviceId.perHour, rateLimiting.otpRequests.perDeviceId.perMinute, rateLimiting.otpRequests.perPhoneNumber.perDay, rateLimiting.otpRequests.perPhoneNumber.perHour, rateLimiting.otpRequests.perPhoneNumber.perMinute, rateLimiting.otpRequests.perWidget.perDay, rateLimiting.otpRequests.perWidget.perHour, rateLimiting.otpRequests.perWidget.perMinute, rateLimiting.widgetAttempts.perDeviceId.perDay, rateLimiting.widgetAttempts.perDeviceId.perHour, rateLimiting.widgetAttempts.perDeviceId.perMinute, rateLimiting.widgetAttempts.perPhoneNumber.perDay, rateLimiting.widgetAttempts.perPhoneNumber.perHour, rateLimiting.widgetAttempts.perPhoneNumber.perMinute, rateLimiting.widgetAttempts.perWidget.perDay, rateLimiting.widgetAttempts.perWidget.perHour, rateLimiting.widgetAttempts.perWidget.perMinute, circuitBreaker.floodThresholds.per15Minutes, circuitBreaker.floodThresholds.per5Minutes, circuitBreaker.floodThresholds.perMinute, circuitBreaker.suspensionDurations.firstViolation, circuitBreaker.suspensionDurations.maxViolation, circuitBreaker.suspensionDurations.secondViolation, circuitBreaker.enabled, expectedUpdatedAt. Tip: Start with the default limits unless your traffic needs different ones. Use traffic recommendations when enough history is available; their calculations include a 10% growth buffer. Final widget recommendations also apply per-phone and per-device caps and may raise per-widget hour and day limits to meet the required ratios, so applied values can differ from the buffered calculation. Review the result against expected peaks before changing live limits. |
--period | enum | last-month | last-quarter | last-6-months | Command-specific option; see the full command details below. |
--smart-routing | enum | on | off | Command-specific option; see the full command details below. Manifest body field: smartRouting, expectedUpdatedAt. |
--passkeys | enum | on | off | Command-specific option; see the full command details below. Manifest body field: passkeyEnabled, expectedUpdatedAt. Tip: Passkeys let returning users verify with Face ID or a fingerprint, and a passkey sign-in can cost up to 40% less than an OTP at the default passkey rate; your account's rate may differ. |
--cluster-gate | enum | on | off | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.enabled, clusterGateIntent, expectedUpdatedAt. |
--cluster-gate-position | enum | cautious | balanced | assertive | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.position, clusterGateIntent, expectedUpdatedAt. Tip: Balanced is the default position. Estimated during an attack, Cautious blocks about 65% of spam and refuses no real users, Balanced about 90% of spam and about 1% of real users, and Assertive 95% or more of spam and about 3% of real users. Assertive is outside Akedly's recommended limits: it does not check whether a request is a real user caught in the same flood. Refusals are rare outside an attack. |
--cluster-gate-escalation | enum | on | off | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.escalation, clusterGateIntent, expectedUpdatedAt. |
--cluster-gate-home-countries | list | — | Command-specific option; see the full command details below. Manifest body field: securityConfig.clusterGate.homeCountries, clusterGateIntent, expectedUpdatedAt. Tip: Declare the countries where you expect phone destinations and end-user IPs to help Cluster Gate understand your traffic. Home countries do not restrict sending or allow or block IP addresses, and other protections still apply. Learned countries stay included automatically; clearing declarations does not clear the learned set. |
--otp-language | enum | en | ar | Command-specific option; see the full command details below. Manifest body field: locale, expectedUpdatedAt. |
--channel-choice | enum | on | off | Command-specific option; see the full command details below. Manifest body field: channelChoiceEnabled, expectedUpdatedAt. |
--channel-choice-mode | enum | ask-first | send-first | Command-specific option; see the full command details below. Manifest body field: channelChoiceMode, expectedUpdatedAt. |
--channel-choice-enforcement | enum | fallback | hard-pin | Command-specific option; see the full command details below. Manifest body field: channelChoiceEnforcement, expectedUpdatedAt. |
--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: widgets:update.
Server request: PUT /widgets/:id.
Consent
--yes when --status or --pipeline-id or --dev-mode or --dev-mode-digits or --rate-limits-file or --circuit-breaker or --circuit-breaker-file or --pow or --pow-trigger-percent or --pow-starting-difficulty or --pow-maximum-difficulty or --recommended-limits or --smart-routing or --passkeys or --cluster-gate or --cluster-gate-position or --cluster-gate-escalation or --cluster-gate-home-countries or --otp-language or --channel-choice or --channel-choice-mode or --channel-choice-enforcement is given. --dry-run bypasses confirmation where supported. Destructive: false. Spends money: false. Real send: false.
Retry
idempotent: Idempotent requests retry network errors and HTTP 429/502/503/504, at most 3 tries, waiting for cooldownSeconds, retryAfter or the Retry-After header (otherwise 1 second) between tries, up to 30 seconds; a longer requested wait is returned as the error.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Edit an owned widget using its 24-hex Mongo _id from akedly widgets list. Requires Full access and widgets:update. Supply at least one editable field; omitted settings are preserved. Use none to clear description or optional scalar branding values, and --pipeline-id none to detach. --status takes active|inactive. Attach a V2 pipeline with both callback URLs, or a V1 pipeline if entitled; V1.2 cannot attach. --rate-limits-file <file> takes a JSON object with widgetAttempts and/or otpRequests, each containing perPhoneNumber, perDevice and/or perWidget, each with perMinute, perHour and/or perDay; for example {"widgetAttempts":{"perWidget":{"perMinute":30,"perHour":150,"perDay":3000}}}. It changes only the named limits. Numbers must be whole and above zero. Per-phone and per-device limits cannot exceed 5/minute, 25/hour or 50/day; each per-widget hour limit must be at least 5 times its minute limit and its day limit at least 100 times its minute limit. Explicit file changes do not automatically recalculate other windows; include every adjustment needed to satisfy the ratios. Inspect details.errors for server validation failures. --circuit-breaker on|off sets whether the breaker is enabled. --circuit-breaker-file <file> accepts floodThresholds with perMinute, per5Minutes and per15Minutes, and suspensionSeconds with firstViolation, secondViolation and maxViolation. Supply only the values to change, as whole numbers above zero; durations are in seconds. The file may be combined with --circuit-breaker, and omitted thresholds and durations are preserved. --pow on|off allows proof-of-work; --pow-trigger-percent takes 25..95, and --pow-starting-difficulty and --pow-maximum-difficulty take 3..6. Starting difficulty must not exceed maximum difficulty. When one difficulty changes, the server compares it with the other supplied or stored value, using 3 for an unset start and 5 for an unset maximum. Unset values are not filled into storage by that comparison; server policy still controls proof-of-work at runtime. Use --company-name, --logo, --favicon-url, --widget-language en|ar, --primary-color and --secondary-color for common branding, or --appearance-file <file> for the appearance object itself, without a branding or appearance wrapper; for example {"theme":{"mode":"dark"}}. The server validates keys and values, including null, empty strings and numeric strings, and refuses unknown keys with APPEARANCE_FIELD_UNKNOWN. Files must be readable regular JSON object files of at most 64 KiB. Appearance keys cannot contain dots or be __proto__, prototype or constructor. Do not supply the same appearance leaf through both a file and a scalar flag; disjoint values may be combined. --recommended-limits applies recommendations based on account-wide widget traffic over --period last-month|last-quarter|last-6-months (default last-month). It replaces all 18 rate limits and enables the circuit breaker with recommended thresholds and suspension durations. The server caps per-phone and per-device limits at 5/minute, 25/hour and 50/day, raises per-widget hour and day limits when needed to at least 5 and 100 times the minute limit, and keeps the breaker minute threshold at least twice the larger per-widget minute limit. It leaves proof-of-work, appearance and breaker state unchanged. NOT_ENOUGH_TRAFFIC means no update was sent; details.confidence and details.analysedRequests explain the result. Recommendations cannot be combined with --rate-limits-file, --circuit-breaker or --circuit-breaker-file. --period works only with --recommended-limits. For rate-limit, breaker or recommendation changes, the CLI first reads the widget and sends the revision it read; this also needs widgets:read. If the widget changes before the save, WIDGET_CHANGED refuses the request; run the command again to read its current settings. A write carrying that revision and the recommendation request made while planning are not automatically retried after network or server errors; the explicit CLI_RATE_LIMITED refusal exception still applies. After an uncertain write result, inspect akedly widgets get <id> before rerunning. Enable dev mode with --dev-mode on --dev-mode-digits 4 --yes; digits require --dev-mode on in the same command. Captcha requirements and cooldowns cannot be changed here. Supplying status, pipeline-id (including none), dev-mode, dev-mode-digits, any rate-limit, breaker or proof-of-work flag, or recommended-limits requires --yes because it can stop, reroute or loosen live verification, even when disabling a protection. Identity and branding changes need no --yes. --dry-run previews without --yes or a write; rate-limit, breaker and recommendation changes still make their planning reads and need the same permissions as a real run. Server validation runs only on the real update. Pipeline settings --passkeys, --smart-routing, --otp-language, --cluster-gate, --cluster-gate-position, --cluster-gate-escalation, --cluster-gate-home-countries, --channel-choice, --channel-choice-mode and --channel-choice-enforcement can be supplied here with --yes. They are written to the resulting attached pipeline after the widget is saved; an attachment change targets the new pipeline. Both write permissions are checked first. Each update carries the revision read during planning. A partial update names the saved and unconfirmed resources; after create, keep the saved Secret and use the provided pipeline update recovery command instead of creating again. --dry-run lists every planned write and saves nothing.
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 widgets update workflow
Change widget identity, appearance, rate limits, circuit breaker, PoW or linked pipeline settings.
akedly widgets update 0123456789abcdef01234567 --name 'Example widget' --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"status": "active",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Synthetic widget settings update from three JSON files
Update one existing widget's request ceilings, flood suspension policy, and appearance together.
akedly widgets update 0123456789abcdef01234567 --rate-limits-file ./fixtures/widget-rate-limits.json --circuit-breaker-file ./fixtures/widget-circuit-breaker.json --appearance-file ./fixtures/widget-appearance.json --yes
Input file: ./fixtures/widget-rate-limits.json (json).
{
"otpRequests": {
"perWidget": {
"perMinute": 30
}
}
}
Input file: ./fixtures/widget-circuit-breaker.json (json).
{
"floodThresholds": {
"perMinute": 20,
"per5Minutes": 60,
"per15Minutes": 120
},
"suspensionSeconds": {
"firstViolation": 60,
"secondViolation": 300,
"maxViolation": 1800
}
}
Input file: ./fixtures/widget-appearance.json (json).
{
"language": "en",
"theme": {
"colors": {
"primary": "#336699",
"secondary": null
}
}
}
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"status": "active",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
Prerequisites: an authorized CLI login and an existing widget owned by the account. The example ID is synthetic; confirm the actual widget with widgets get first.
The input files set widget OTP-request limits, circuit-breaker thresholds and suspension seconds, and appearance. The CLI reads the current widget revision before saving. Do not combine these explicit limits with --recommended-limits.
After success, inspect widgets get. If the command reports PARTIAL_UPDATE, inspect the widget and each linked resource listed in applied/notApplied before retrying.
Additional command forms
akedly widgets update <id> --company-name Example --widget-language en
akedly widgets update <id> --dev-mode on --dev-mode-digits 4 --yes
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"validation": "Use the 24-hex Mongo widget _id.",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "name",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
]
},
{
"name": "description",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "description",
"transform": "none-empty",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
]
},
{
"name": "pipeline-id",
"schema": {
"type": "string",
"pattern": "^(?:[a-fA-F0-9]{24}|none)$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "pipelineId",
"transform": "none-null",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"validation": {
"syntax": "24-digit hexadecimal object ID, or lowercase none to clear."
}
},
{
"name": "status",
"schema": {
"type": "string",
"enum": [
"active",
"inactive"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "status",
"transform": "identity",
"role": "body",
"input": "status",
"contributors": [
"status"
]
}
]
},
{
"name": "logo",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.logo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
},
{
"path": "branding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
}
]
},
{
"name": "company-name",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.companyName",
"transform": "none-empty",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
},
{
"path": "branding.customCompanyName",
"transform": "company-name-mirror",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
}
]
},
{
"name": "favicon-url",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.faviconUrl",
"transform": "none-empty",
"role": "body",
"input": "favicon-url",
"contributors": [
"favicon-url"
]
}
]
},
{
"name": "widget-language",
"schema": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"required": false,
"wire": [
{
"path": "branding.appearance.language",
"transform": "identity",
"role": "body",
"input": "widget-language",
"contributors": [
"widget-language"
]
}
]
},
{
"name": "primary-color",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.appearance.theme.colors.primary",
"transform": "none-empty",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
},
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
}
]
},
{
"name": "secondary-color",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "branding.appearance.theme.colors.secondary",
"transform": "none-empty",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
},
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
}
]
},
{
"name": "dev-mode",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "devMode.enabled",
"transform": "identity",
"role": "body",
"input": "dev-mode",
"contributors": [
"dev-mode"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "dev-mode-digits",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[4-6])(?:\\.0+)?)$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "devMode.digits",
"transform": "identity",
"role": "body",
"input": "dev-mode-digits",
"contributors": [
"dev-mode-digits"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 4,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 4,
"maximum": 6
}
}
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "confirmation",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "confirmation",
"contributors": [
"yes"
]
}
]
},
{
"name": "rate-limits-file",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "input-file",
"contract": "widget-rate-limits",
"requestTargets": [
{
"role": "body",
"path": "$",
"transform": "json-fields",
"fileContract": "widget-rate-limits"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision"
}
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"schema": {
"$ref": "#/$defs/widgetRateLimits",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"windows": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"additionalProperties": false
},
"dimensions": {
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/windows"
},
"perDevice": {
"$ref": "#/$defs/windows"
},
"perWidget": {
"$ref": "#/$defs/windows"
}
},
"additionalProperties": false
},
"widgetRateLimits": {
"type": "object",
"description": "Each perPhoneNumber/perDevice limit is capped by the server at 5 per minute, 25 per hour and 50 per day. If perWidget perMinute is supplied, perHour must be at least 5 times it and perDay at least 100 times it when those windows are supplied; the server validates the merged settings for updates.",
"properties": {
"widgetAttempts": {
"$ref": "#/$defs/dimensions"
},
"otpRequests": {
"$ref": "#/$defs/dimensions"
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"otpRequests": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"widgetAttempts": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"otpRequests.perDevice.perDay": {
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perHour": {
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perMinute": {
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perDay": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perHour": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perMinute": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perDay": {
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perHour": {
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perMinute": {
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perDay": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perHour": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perMinute": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perDay": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perHour": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perMinute": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perDay": {
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perHour": {
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perMinute": {
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"semanticSchema": {
"$ref": "#/$defs/widgetRateLimits",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"windows": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"additionalProperties": false
},
"dimensions": {
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/windows"
},
"perDevice": {
"$ref": "#/$defs/windows"
},
"perWidget": {
"$ref": "#/$defs/windows"
}
},
"additionalProperties": false
},
"widgetRateLimits": {
"type": "object",
"description": "Each perPhoneNumber/perDevice limit is capped by the server at 5 per minute, 25 per hour and 50 per day. If perWidget perMinute is supplied, perHour must be at least 5 times it and perDay at least 100 times it when those windows are supplied; the server validates the merged settings for updates.",
"properties": {
"widgetAttempts": {
"$ref": "#/$defs/dimensions"
},
"otpRequests": {
"$ref": "#/$defs/dimensions"
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "rate-limits-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-rate-limits",
"contributors": [
"rate-limits-file"
]
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perDevice.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perPhoneNumber.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "otpRequests.perWidget.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perDevice.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perPhoneNumber.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perDay",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perHour",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"fileContract": "widget-rate-limits",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "widgetAttempts.perWidget.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"input": "rate-limits-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"fileContract": "widget-rate-limits"
}
]
},
{
"name": "circuit-breaker",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "circuit-breaker-file",
"schema": {
"type": "string"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "input-file",
"contract": "widget-circuit-breaker",
"requestTargets": [
{
"role": "body",
"path": "$",
"transform": "json-fields",
"fileContract": "widget-circuit-breaker"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision"
}
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"schema": {
"$ref": "#/$defs/widgetBreaker",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"widgetBreaker": {
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"per15Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"per5Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"maxViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"secondViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"floodThresholds.per15Minutes": {
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.per5Minutes": {
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.perMinute": {
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"suspensionSeconds.firstViolation": {
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.maxViolation": {
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.secondViolation": {
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"semanticSchema": {
"$ref": "#/$defs/widgetBreaker",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"widgetBreaker": {
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false
}
}
},
"validationBoundary": "semanticSchema describes curated command file-content constraints; localReaderSchema describes file-reader constraints. Read command help and schema descriptions for additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "circuit-breaker-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"contributors": [
"circuit-breaker-file"
]
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.per15Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.per5Minutes",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "floodThresholds.perMinute",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.firstViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.maxViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"fileContract": "widget-circuit-breaker",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"valueType": "positive-integer",
"fileKey": "suspensionSeconds.secondViolation",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"input": "circuit-breaker-file",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"fileContract": "widget-circuit-breaker"
}
]
},
{
"name": "pow",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "security.pow.allowed",
"transform": "identity",
"role": "body",
"input": "pow",
"contributors": [
"pow"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "pow-trigger-percent",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:(?:2[5-9]|[3-8]\\d{1}|9[0-5]))(?:\\.0+)?)$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "security.pow.triggerPercent",
"transform": "identity",
"role": "body",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 25,
"maximum": 95,
"parsedValueSchema": {
"type": "integer",
"minimum": 25,
"maximum": 95
}
}
},
{
"name": "pow-starting-difficulty",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[3-6])(?:\\.0+)?)$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "security.pow.startDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 3,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 3,
"maximum": 6
}
}
},
{
"name": "pow-maximum-difficulty",
"schema": {
"type": "string",
"pattern": "^(?:\\+?0*(?:[3-6])(?:\\.0+)?)$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "security.pow.maxDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
}
],
"validation": {
"syntax": "Decimal integer token.",
"minimum": 3,
"maximum": 6,
"parsedValueSchema": {
"type": "integer",
"minimum": 3,
"maximum": 6
}
}
},
{
"name": "appearance-file",
"schema": {
"type": "string"
},
"required": false,
"input": {
"role": "input-file",
"contract": "widget-appearance",
"requestTargets": [
{
"role": "body",
"path": "branding.appearance",
"transform": "json-object",
"fileContract": "widget-appearance"
}
],
"schema": {
"$ref": "#/$defs/appearance",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"hex": {
"anyOf": [
{
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"enumOrUnset": {
"type": [
"string",
"null"
]
},
"numOrUnset": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"appearance": {
"type": "object",
"description": "Known appearance keys and value checks are modeled below. The CLI forwards the appearance object; unknown keys are reported as APPEARANCE_FIELD_UNKNOWN and the backend's dashboard-rule policy determines whether they are refused or allowed in report mode. Keys with dots or __proto__/prototype/constructor are rejected locally.",
"properties": {
"language": {
"type": [
"string",
"null"
],
"enum": [
"en",
"ar",
null,
""
]
},
"preset": {
"type": [
"string",
"null"
],
"maxLength": 120
},
"theme": {
"$ref": "#/$defs/theme",
"type": [
"object",
"null"
],
"description": "Appearance theme; null replaces the saved theme section, after which appearance normalization supplies its defaults."
},
"layout": {
"$ref": "#/$defs/layout",
"type": [
"object",
"null"
],
"description": "Appearance layout; null replaces the saved layout section, after which appearance normalization supplies its defaults."
},
"components": {
"$ref": "#/$defs/components",
"type": [
"object",
"null"
],
"description": "Appearance components; null replaces the saved components section, after which appearance normalization supplies its defaults."
},
"copy": {
"$ref": "#/$defs/copy",
"type": [
"object",
"null"
],
"description": "Appearance copy; null replaces the saved copy section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"theme": {
"type": [
"object",
"null"
],
"properties": {
"mode": {
"type": [
"string",
"null"
],
"enum": [
"light",
"dark",
"system",
null,
""
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"density": {
"type": [
"string",
"null"
],
"enum": [
"compact",
"comfortable",
"spacious",
null,
""
]
},
"densityScale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0.6,
"maximum": 1.6
},
"colors": {
"type": [
"object",
"null"
],
"properties": {
"primary": {
"$ref": "#/$defs/hex"
},
"secondary": {
"$ref": "#/$defs/hex"
},
"accent": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
},
"foreground": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Theme colors; null replaces the saved colors section, after which appearance normalization supplies its defaults."
},
"font": {
"type": [
"object",
"null"
],
"properties": {
"family": {
"type": [
"string",
"null"
],
"enum": [
"system",
"inter",
"roboto",
"open-sans",
"lato",
"montserrat",
"poppins",
"nunito",
"nunito-sans",
"work-sans",
"dm-sans",
"sora",
"space-grotesk",
"manrope",
"rubik",
"mulish",
"karla",
"plus-jakarta-sans",
"figtree",
"outfit",
"albert-sans",
"public-sans",
"ibm-plex-sans",
"source-sans-3",
"pt-sans",
"raleway",
"quicksand",
"josefin-sans",
"barlow",
"cabin",
"heebo",
"assistant",
"red-hat-display",
"lexend",
"onest",
"geist",
"schibsted-grotesk",
"instrument-sans",
"be-vietnam-pro",
"merriweather",
"lora",
"playfair-display",
"source-serif-4",
"pt-serif",
"bitter",
"jetbrains-mono",
"ibm-plex-mono",
"space-mono",
"cairo",
"tajawal",
"ibm-plex-sans-arabic",
"noto-sans-arabic",
null
]
},
"baseSize": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"headingWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 400,
"maximum": 900
},
"bodyWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 700
}
},
"additionalProperties": true,
"description": "Theme font; null replaces the saved font section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"layout": {
"type": [
"object",
"null"
],
"properties": {
"template": {
"type": [
"string",
"null"
],
"enum": [
"centered",
"split-left",
"split-right",
"compact",
null,
""
]
},
"logoPosition": {
"type": [
"string",
"null"
],
"enum": [
"top",
"bottom",
"inline",
null,
""
]
},
"align": {
"type": [
"string",
"null"
],
"enum": [
"center",
"left",
null,
""
]
},
"panel": {
"type": [
"object",
"null"
],
"properties": {
"background": {
"$ref": "#/$defs/hex"
},
"imageUrl": {
"anyOf": [
{
"type": "string",
"format": "uri",
"pattern": "^https://"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"tagline": {
"type": [
"string",
"null"
],
"maxLength": 120
}
},
"additionalProperties": true,
"description": "Layout panel; null replaces the saved panel section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"components": {
"type": [
"object",
"null"
],
"properties": {
"button": {
"type": [
"object",
"null"
],
"properties": {
"variant": {
"type": [
"string",
"null"
],
"enum": [
"solid",
"outline",
"soft",
null,
""
]
},
"size": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"textColor": {
"$ref": "#/$defs/hex"
},
"fullWidth": {
"type": [
"boolean",
"null"
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"scale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 1,
"maximum": 1.08
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "Button component settings; null replaces that section and appearance normalization supplies defaults."
},
"otp": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"gap": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 24
},
"activeColor": {
"$ref": "#/$defs/hex"
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "OTP component settings; null replaces that section and appearance normalization supplies defaults."
},
"card": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"border": {
"type": [
"boolean",
"null"
]
},
"ring": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Card component settings; null replaces that section and appearance normalization supplies defaults."
},
"link": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"weight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 800
},
"underline": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Link component settings; null replaces that section and appearance normalization supplies defaults."
},
"success": {
"type": [
"object",
"null"
],
"properties": {
"confetti": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Success component settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
},
"copy": {
"type": [
"object",
"null"
],
"properties": {
"default": {
"type": [
"object",
"null"
],
"properties": {
"title": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"subtitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"ctaLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"resendLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successTitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"errorMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
}
},
"additionalProperties": true,
"description": "Default copy map; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
}
}
},
"localReaderSchema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"JsonValue": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"$ref": "#/$defs/JsonObject"
},
{
"$ref": "#/$defs/JsonArray"
}
]
},
"JsonObject": {
"type": "object",
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
},
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
}
},
"JsonArray": {
"type": "array",
"items": {
"$ref": "#/$defs/JsonValue"
}
}
},
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
}
},
"fileContract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"language": {
"path": "branding.appearance.language",
"transform": "identity"
},
"theme.colors.primary": {
"path": "branding.appearance.theme.colors.primary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror"
}
]
},
"theme.colors.secondary": {
"path": "branding.appearance.theme.colors.secondary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror"
}
]
},
"theme.mode": {
"path": "branding.appearance.theme.mode",
"transform": "identity",
"mirrors": [
{
"path": "branding.theme",
"transform": "identity"
}
]
}
},
"settings": true,
"resource": "widget",
"objectPath": "branding.appearance",
"serverValidated": true
},
"semanticSchema": {
"$ref": "#/$defs/appearance",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"hex": {
"anyOf": [
{
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"enumOrUnset": {
"type": [
"string",
"null"
]
},
"numOrUnset": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"appearance": {
"type": "object",
"description": "Known appearance keys and value checks are modeled below. The CLI forwards the appearance object; unknown keys are reported as APPEARANCE_FIELD_UNKNOWN and the backend's dashboard-rule policy determines whether they are refused or allowed in report mode. Keys with dots or __proto__/prototype/constructor are rejected locally.",
"properties": {
"language": {
"type": [
"string",
"null"
],
"enum": [
"en",
"ar",
null,
""
]
},
"preset": {
"type": [
"string",
"null"
],
"maxLength": 120
},
"theme": {
"$ref": "#/$defs/theme",
"type": [
"object",
"null"
],
"description": "Appearance theme; null replaces the saved theme section, after which appearance normalization supplies its defaults."
},
"layout": {
"$ref": "#/$defs/layout",
"type": [
"object",
"null"
],
"description": "Appearance layout; null replaces the saved layout section, after which appearance normalization supplies its defaults."
},
"components": {
"$ref": "#/$defs/components",
"type": [
"object",
"null"
],
"description": "Appearance components; null replaces the saved components section, after which appearance normalization supplies its defaults."
},
"copy": {
"$ref": "#/$defs/copy",
"type": [
"object",
"null"
],
"description": "Appearance copy; null replaces the saved copy section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"theme": {
"type": [
"object",
"null"
],
"properties": {
"mode": {
"type": [
"string",
"null"
],
"enum": [
"light",
"dark",
"system",
null,
""
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"density": {
"type": [
"string",
"null"
],
"enum": [
"compact",
"comfortable",
"spacious",
null,
""
]
},
"densityScale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0.6,
"maximum": 1.6
},
"colors": {
"type": [
"object",
"null"
],
"properties": {
"primary": {
"$ref": "#/$defs/hex"
},
"secondary": {
"$ref": "#/$defs/hex"
},
"accent": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
},
"foreground": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Theme colors; null replaces the saved colors section, after which appearance normalization supplies its defaults."
},
"font": {
"type": [
"object",
"null"
],
"properties": {
"family": {
"type": [
"string",
"null"
],
"enum": [
"system",
"inter",
"roboto",
"open-sans",
"lato",
"montserrat",
"poppins",
"nunito",
"nunito-sans",
"work-sans",
"dm-sans",
"sora",
"space-grotesk",
"manrope",
"rubik",
"mulish",
"karla",
"plus-jakarta-sans",
"figtree",
"outfit",
"albert-sans",
"public-sans",
"ibm-plex-sans",
"source-sans-3",
"pt-sans",
"raleway",
"quicksand",
"josefin-sans",
"barlow",
"cabin",
"heebo",
"assistant",
"red-hat-display",
"lexend",
"onest",
"geist",
"schibsted-grotesk",
"instrument-sans",
"be-vietnam-pro",
"merriweather",
"lora",
"playfair-display",
"source-serif-4",
"pt-serif",
"bitter",
"jetbrains-mono",
"ibm-plex-mono",
"space-mono",
"cairo",
"tajawal",
"ibm-plex-sans-arabic",
"noto-sans-arabic",
null
]
},
"baseSize": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"headingWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 400,
"maximum": 900
},
"bodyWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 700
}
},
"additionalProperties": true,
"description": "Theme font; null replaces the saved font section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"layout": {
"type": [
"object",
"null"
],
"properties": {
"template": {
"type": [
"string",
"null"
],
"enum": [
"centered",
"split-left",
"split-right",
"compact",
null,
""
]
},
"logoPosition": {
"type": [
"string",
"null"
],
"enum": [
"top",
"bottom",
"inline",
null,
""
]
},
"align": {
"type": [
"string",
"null"
],
"enum": [
"center",
"left",
null,
""
]
},
"panel": {
"type": [
"object",
"null"
],
"properties": {
"background": {
"$ref": "#/$defs/hex"
},
"imageUrl": {
"anyOf": [
{
"type": "string",
"format": "uri",
"pattern": "^https://"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"tagline": {
"type": [
"string",
"null"
],
"maxLength": 120
}
},
"additionalProperties": true,
"description": "Layout panel; null replaces the saved panel section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"components": {
"type": [
"object",
"null"
],
"properties": {
"button": {
"type": [
"object",
"null"
],
"properties": {
"variant": {
"type": [
"string",
"null"
],
"enum": [
"solid",
"outline",
"soft",
null,
""
]
},
"size": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"textColor": {
"$ref": "#/$defs/hex"
},
"fullWidth": {
"type": [
"boolean",
"null"
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"scale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 1,
"maximum": 1.08
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "Button component settings; null replaces that section and appearance normalization supplies defaults."
},
"otp": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"gap": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 24
},
"activeColor": {
"$ref": "#/$defs/hex"
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "OTP component settings; null replaces that section and appearance normalization supplies defaults."
},
"card": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"border": {
"type": [
"boolean",
"null"
]
},
"ring": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Card component settings; null replaces that section and appearance normalization supplies defaults."
},
"link": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"weight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 800
},
"underline": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Link component settings; null replaces that section and appearance normalization supplies defaults."
},
"success": {
"type": [
"object",
"null"
],
"properties": {
"confetti": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Success component settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
},
"copy": {
"type": [
"object",
"null"
],
"properties": {
"default": {
"type": [
"object",
"null"
],
"properties": {
"title": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"subtitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"ctaLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"resendLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successTitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"errorMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
}
},
"additionalProperties": true,
"description": "Default copy map; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
}
}
},
"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": "appearance-file",
"inputRole": "input-file",
"transform": "identity",
"fileContract": "widget-appearance",
"contributors": [
"appearance-file"
]
},
{
"role": "body",
"path": "branding.appearance",
"transform": "json-object",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
]
},
{
"role": "body",
"path": "branding.appearance.language",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "language",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.appearance.theme.colors.primary",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.primary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.primary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.appearance.theme.colors.secondary",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.secondary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.colors.secondary",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.appearance.theme.mode",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.mode",
"inputTransform": "json-fields"
},
{
"role": "body",
"path": "branding.theme",
"transform": "identity",
"fileContract": "widget-appearance",
"input": "appearance-file",
"contributors": [
"appearance-file"
],
"fileKey": "theme.mode",
"inputTransform": "json-fields"
}
]
},
{
"name": "recommended-limits",
"schema": {
"type": "boolean"
},
"required": false,
"requiresConsent": true,
"input": {
"role": "handler-built",
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
},
"targets": [
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "circuitBreaker.enabled",
"transform": "widget-recommended-value"
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision"
}
],
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "circuitBreaker.enabled",
"transform": "widget-recommended-value",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"role": "body",
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"input": "recommended-limits",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
}
]
},
{
"name": "period",
"schema": {
"type": "string",
"enum": [
"last-month",
"last-quarter",
"last-6-months"
]
},
"required": false,
"input": {
"role": "local-control",
"purpose": "recommendation-period",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "period",
"inputRole": "local-control",
"transform": "identity",
"purpose": "recommendation-period",
"contributors": [
"period"
]
}
]
},
{
"name": "smart-routing",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "smartRouting",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "passkeys",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "passkeyEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.enabled",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate-position",
"schema": {
"type": "string",
"enum": [
"cautious",
"balanced",
"assertive"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.position",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
]
},
{
"name": "cluster-gate-escalation",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.escalation",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "cluster-gate-home-countries",
"schema": {
"type": "string",
"pattern": "^(?:[^,]*[^,\\s][^,]*)(?:,(?:[^,]*[^,\\s][^,]*))*$"
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"validation": {
"syntax": "Comma-separated values; every segment must contain at least one non-whitespace character."
}
},
{
"name": "otp-language",
"schema": {
"type": "string",
"enum": [
"en",
"ar"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "locale",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
]
},
{
"name": "channel-choice",
"schema": {
"type": "string",
"enum": [
"on",
"off"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"valueTranslations": {
"on": true,
"off": false
}
},
{
"name": "channel-choice-mode",
"schema": {
"type": "string",
"enum": [
"ask-first",
"send-first"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceMode",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"valueTranslations": {
"ask-first": "ask_first",
"send-first": "send_first"
}
},
{
"name": "channel-choice-enforcement",
"schema": {
"type": "string",
"enum": [
"fallback",
"hard-pin"
]
},
"required": false,
"requiresConsent": true,
"wire": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
],
"valueTranslations": {
"fallback": "fallback",
"hard-pin": "hard_pin"
}
}
],
"files": [
{
"name": "widget-rate-limits",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"otpRequests.perDevice.perDay": {
"path": "rateLimiting.otpRequests.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perHour": {
"path": "rateLimiting.otpRequests.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perDevice.perMinute": {
"path": "rateLimiting.otpRequests.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perDay": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perHour": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perPhoneNumber.perMinute": {
"path": "rateLimiting.otpRequests.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perDay": {
"path": "rateLimiting.otpRequests.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perHour": {
"path": "rateLimiting.otpRequests.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"otpRequests.perWidget.perMinute": {
"path": "rateLimiting.otpRequests.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perDay": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perHour": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perDevice.perMinute": {
"path": "rateLimiting.widgetAttempts.perDeviceId.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perDay": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perHour": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perPhoneNumber.perMinute": {
"path": "rateLimiting.widgetAttempts.perPhoneNumber.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perDay": {
"path": "rateLimiting.widgetAttempts.perWidget.perDay",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perHour": {
"path": "rateLimiting.widgetAttempts.perWidget.perHour",
"transform": "identity",
"valueType": "positive-integer"
},
"widgetAttempts.perWidget.perMinute": {
"path": "rateLimiting.widgetAttempts.perWidget.perMinute",
"transform": "identity",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"otpRequests": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"widgetAttempts": {
"type": "object",
"properties": {
"perDevice": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perPhoneNumber": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"perWidget": {
"type": "object",
"properties": {
"perDay": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perHour": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"uses": [
{
"flag": "rate-limits-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$ref": "#/$defs/widgetRateLimits",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"windows": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perHour": {
"$ref": "#/$defs/positiveSafeInteger"
},
"perDay": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"additionalProperties": false
},
"dimensions": {
"type": "object",
"properties": {
"perPhoneNumber": {
"$ref": "#/$defs/windows"
},
"perDevice": {
"$ref": "#/$defs/windows"
},
"perWidget": {
"$ref": "#/$defs/windows"
}
},
"additionalProperties": false
},
"widgetRateLimits": {
"type": "object",
"description": "Each perPhoneNumber/perDevice limit is capped by the server at 5 per minute, 25 per hour and 50 per day. If perWidget perMinute is supplied, perHour must be at least 5 times it and perDay at least 100 times it when those windows are supplied; the server validates the merged settings for updates.",
"properties": {
"widgetAttempts": {
"$ref": "#/$defs/dimensions"
},
"otpRequests": {
"$ref": "#/$defs/dimensions"
}
},
"minProperties": 1,
"additionalProperties": false
}
}
}
}
]
},
{
"name": "widget-circuit-breaker",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"floodThresholds.per15Minutes": {
"path": "circuitBreaker.floodThresholds.per15Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.per5Minutes": {
"path": "circuitBreaker.floodThresholds.per5Minutes",
"transform": "identity",
"valueType": "positive-integer"
},
"floodThresholds.perMinute": {
"path": "circuitBreaker.floodThresholds.perMinute",
"transform": "identity",
"valueType": "positive-integer"
},
"suspensionSeconds.firstViolation": {
"path": "circuitBreaker.suspensionDurations.firstViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.maxViolation": {
"path": "circuitBreaker.suspensionDurations.maxViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
},
"suspensionSeconds.secondViolation": {
"path": "circuitBreaker.suspensionDurations.secondViolation",
"transform": "seconds-milliseconds",
"valueType": "positive-integer"
}
},
"settings": true,
"resource": "widget"
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"per15Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"per5Minutes": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"perMinute": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
}
},
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"maxViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"secondViolation": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"uses": [
{
"flag": "circuit-breaker-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$ref": "#/$defs/widgetBreaker",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"positiveSafeInteger": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740991
},
"seconds": {
"type": "integer",
"minimum": 1,
"maximum": 9007199254740
},
"widgetBreaker": {
"type": "object",
"properties": {
"floodThresholds": {
"type": "object",
"properties": {
"perMinute": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per5Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
},
"per15Minutes": {
"$ref": "#/$defs/positiveSafeInteger"
}
},
"minProperties": 1,
"additionalProperties": false
},
"suspensionSeconds": {
"type": "object",
"properties": {
"firstViolation": {
"$ref": "#/$defs/seconds"
},
"secondViolation": {
"$ref": "#/$defs/seconds"
},
"maxViolation": {
"$ref": "#/$defs/seconds"
}
},
"minProperties": 1,
"additionalProperties": false
}
},
"minProperties": 1,
"additionalProperties": false
}
}
}
}
]
},
{
"name": "widget-appearance",
"contract": {
"format": "json-object",
"maxBytes": 65536,
"regularFile": true,
"reader": "files.js:readFlagFile",
"type": "json-object",
"exclusive": false,
"mode": null,
"trim": false,
"writer": null,
"minBytes": 0,
"detectChanges": true,
"fieldMappings": {
"language": {
"path": "branding.appearance.language",
"transform": "identity"
},
"theme.colors.primary": {
"path": "branding.appearance.theme.colors.primary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror"
}
]
},
"theme.colors.secondary": {
"path": "branding.appearance.theme.colors.secondary",
"transform": "identity",
"mirrors": [
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror"
}
]
},
"theme.mode": {
"path": "branding.appearance.theme.mode",
"transform": "identity",
"mirrors": [
{
"path": "branding.theme",
"transform": "identity"
}
]
}
},
"settings": true,
"resource": "widget",
"objectPath": "branding.appearance",
"serverValidated": true
},
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"JsonValue": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
},
{
"type": "boolean"
},
{
"type": "null"
},
{
"$ref": "#/$defs/JsonObject"
},
{
"$ref": "#/$defs/JsonArray"
}
]
},
"JsonObject": {
"type": "object",
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
},
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
}
},
"JsonArray": {
"type": "array",
"items": {
"$ref": "#/$defs/JsonValue"
}
}
},
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/JsonValue"
},
"propertyNames": {
"pattern": "^(?!.*\\.)(?!__proto__$)(?!prototype$)(?!constructor$).*$"
}
},
"uses": [
{
"flag": "appearance-file",
"role": "input-file",
"purpose": null,
"semanticSchema": {
"$ref": "#/$defs/appearance",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$defs": {
"hex": {
"anyOf": [
{
"type": "string",
"pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"enumOrUnset": {
"type": [
"string",
"null"
]
},
"numOrUnset": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?$"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"appearance": {
"type": "object",
"description": "Known appearance keys and value checks are modeled below. The CLI forwards the appearance object; unknown keys are reported as APPEARANCE_FIELD_UNKNOWN and the backend's dashboard-rule policy determines whether they are refused or allowed in report mode. Keys with dots or __proto__/prototype/constructor are rejected locally.",
"properties": {
"language": {
"type": [
"string",
"null"
],
"enum": [
"en",
"ar",
null,
""
]
},
"preset": {
"type": [
"string",
"null"
],
"maxLength": 120
},
"theme": {
"$ref": "#/$defs/theme",
"type": [
"object",
"null"
],
"description": "Appearance theme; null replaces the saved theme section, after which appearance normalization supplies its defaults."
},
"layout": {
"$ref": "#/$defs/layout",
"type": [
"object",
"null"
],
"description": "Appearance layout; null replaces the saved layout section, after which appearance normalization supplies its defaults."
},
"components": {
"$ref": "#/$defs/components",
"type": [
"object",
"null"
],
"description": "Appearance components; null replaces the saved components section, after which appearance normalization supplies its defaults."
},
"copy": {
"$ref": "#/$defs/copy",
"type": [
"object",
"null"
],
"description": "Appearance copy; null replaces the saved copy section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"theme": {
"type": [
"object",
"null"
],
"properties": {
"mode": {
"type": [
"string",
"null"
],
"enum": [
"light",
"dark",
"system",
null,
""
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"density": {
"type": [
"string",
"null"
],
"enum": [
"compact",
"comfortable",
"spacious",
null,
""
]
},
"densityScale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0.6,
"maximum": 1.6
},
"colors": {
"type": [
"object",
"null"
],
"properties": {
"primary": {
"$ref": "#/$defs/hex"
},
"secondary": {
"$ref": "#/$defs/hex"
},
"accent": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
},
"foreground": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Theme colors; null replaces the saved colors section, after which appearance normalization supplies its defaults."
},
"font": {
"type": [
"object",
"null"
],
"properties": {
"family": {
"type": [
"string",
"null"
],
"enum": [
"system",
"inter",
"roboto",
"open-sans",
"lato",
"montserrat",
"poppins",
"nunito",
"nunito-sans",
"work-sans",
"dm-sans",
"sora",
"space-grotesk",
"manrope",
"rubik",
"mulish",
"karla",
"plus-jakarta-sans",
"figtree",
"outfit",
"albert-sans",
"public-sans",
"ibm-plex-sans",
"source-sans-3",
"pt-sans",
"raleway",
"quicksand",
"josefin-sans",
"barlow",
"cabin",
"heebo",
"assistant",
"red-hat-display",
"lexend",
"onest",
"geist",
"schibsted-grotesk",
"instrument-sans",
"be-vietnam-pro",
"merriweather",
"lora",
"playfair-display",
"source-serif-4",
"pt-serif",
"bitter",
"jetbrains-mono",
"ibm-plex-mono",
"space-mono",
"cairo",
"tajawal",
"ibm-plex-sans-arabic",
"noto-sans-arabic",
null
]
},
"baseSize": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"headingWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 400,
"maximum": 900
},
"bodyWeight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 700
}
},
"additionalProperties": true,
"description": "Theme font; null replaces the saved font section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"layout": {
"type": [
"object",
"null"
],
"properties": {
"template": {
"type": [
"string",
"null"
],
"enum": [
"centered",
"split-left",
"split-right",
"compact",
null,
""
]
},
"logoPosition": {
"type": [
"string",
"null"
],
"enum": [
"top",
"bottom",
"inline",
null,
""
]
},
"align": {
"type": [
"string",
"null"
],
"enum": [
"center",
"left",
null,
""
]
},
"panel": {
"type": [
"object",
"null"
],
"properties": {
"background": {
"$ref": "#/$defs/hex"
},
"imageUrl": {
"anyOf": [
{
"type": "string",
"format": "uri",
"pattern": "^https://"
},
{
"type": "null"
},
{
"const": ""
}
]
},
"tagline": {
"type": [
"string",
"null"
],
"maxLength": 120
}
},
"additionalProperties": true,
"description": "Layout panel; null replaces the saved panel section, after which appearance normalization supplies its defaults."
}
},
"additionalProperties": true
},
"components": {
"type": [
"object",
"null"
],
"properties": {
"button": {
"type": [
"object",
"null"
],
"properties": {
"variant": {
"type": [
"string",
"null"
],
"enum": [
"solid",
"outline",
"soft",
null,
""
]
},
"size": {
"type": [
"string",
"null"
],
"enum": [
"sm",
"md",
"lg",
null,
""
]
},
"textColor": {
"$ref": "#/$defs/hex"
},
"fullWidth": {
"type": [
"boolean",
"null"
]
},
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"scale": {
"$ref": "#/$defs/numOrUnset",
"minimum": 1,
"maximum": 1.08
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "Button component settings; null replaces that section and appearance normalization supplies defaults."
},
"otp": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"gap": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 24
},
"activeColor": {
"$ref": "#/$defs/hex"
},
"hover": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"transitionMs": {
"$ref": "#/$defs/numOrUnset",
"minimum": 0,
"maximum": 1000
}
},
"additionalProperties": true,
"description": "Hover settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true,
"description": "OTP component settings; null replaces that section and appearance normalization supplies defaults."
},
"card": {
"type": [
"object",
"null"
],
"properties": {
"radius": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
"full",
null,
""
]
},
"shadow": {
"type": [
"string",
"null"
],
"enum": [
"none",
"sm",
"md",
"lg",
"xl",
null,
""
]
},
"border": {
"type": [
"boolean",
"null"
]
},
"ring": {
"$ref": "#/$defs/hex"
},
"background": {
"$ref": "#/$defs/hex"
}
},
"additionalProperties": true,
"description": "Card component settings; null replaces that section and appearance normalization supplies defaults."
},
"link": {
"type": [
"object",
"null"
],
"properties": {
"color": {
"$ref": "#/$defs/hex"
},
"weight": {
"$ref": "#/$defs/numOrUnset",
"minimum": 300,
"maximum": 800
},
"underline": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Link component settings; null replaces that section and appearance normalization supplies defaults."
},
"success": {
"type": [
"object",
"null"
],
"properties": {
"confetti": {
"type": [
"boolean",
"null"
]
}
},
"additionalProperties": true,
"description": "Success component settings; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
},
"copy": {
"type": [
"object",
"null"
],
"properties": {
"default": {
"type": [
"object",
"null"
],
"properties": {
"title": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"subtitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"ctaLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"resendLabel": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successTitle": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"successMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
},
"errorMessage": {
"type": [
"string",
"null"
],
"maxLength": 280
}
},
"additionalProperties": true,
"description": "Default copy map; null replaces that section and appearance normalization supplies defaults."
}
},
"additionalProperties": true
}
}
}
}
]
}
],
"wire": {
"method": "PUT",
"route": "/widgets/:id",
"query": {},
"body": {
"name": [
{
"path": "name",
"transform": "trim",
"role": "body",
"input": "name",
"contributors": [
"name"
]
}
],
"description": [
{
"path": "description",
"transform": "none-empty",
"role": "body",
"input": "description",
"contributors": [
"description"
]
}
],
"pipeline-id": [
{
"path": "pipelineId",
"transform": "none-null",
"role": "body",
"input": "pipeline-id",
"contributors": [
"pipeline-id"
]
}
],
"status": [
{
"path": "status",
"transform": "identity",
"role": "body",
"input": "status",
"contributors": [
"status"
]
}
],
"logo": [
{
"path": "branding.logo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
},
{
"path": "branding.customLogo",
"transform": "none-empty",
"role": "body",
"input": "logo",
"contributors": [
"logo"
]
}
],
"company-name": [
{
"path": "branding.companyName",
"transform": "none-empty",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
},
{
"path": "branding.customCompanyName",
"transform": "company-name-mirror",
"role": "body",
"input": "company-name",
"contributors": [
"company-name"
]
}
],
"favicon-url": [
{
"path": "branding.faviconUrl",
"transform": "none-empty",
"role": "body",
"input": "favicon-url",
"contributors": [
"favicon-url"
]
}
],
"widget-language": [
{
"path": "branding.appearance.language",
"transform": "identity",
"role": "body",
"input": "widget-language",
"contributors": [
"widget-language"
]
}
],
"primary-color": [
{
"path": "branding.appearance.theme.colors.primary",
"transform": "none-empty",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
},
{
"path": "branding.customPrimaryColor",
"transform": "primary-color-mirror",
"role": "body",
"input": "primary-color",
"contributors": [
"primary-color"
]
}
],
"secondary-color": [
{
"path": "branding.appearance.theme.colors.secondary",
"transform": "none-empty",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
},
{
"path": "branding.customSecondaryColor",
"transform": "secondary-color-mirror",
"role": "body",
"input": "secondary-color",
"contributors": [
"secondary-color"
]
}
],
"dev-mode": [
{
"path": "devMode.enabled",
"transform": "identity",
"role": "body",
"input": "dev-mode",
"contributors": [
"dev-mode"
]
}
],
"dev-mode-digits": [
{
"path": "devMode.digits",
"transform": "identity",
"role": "body",
"input": "dev-mode-digits",
"contributors": [
"dev-mode-digits"
]
}
],
"circuit-breaker": [
{
"path": "circuitBreaker.enabled",
"transform": "identity",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"role": "body",
"input": "circuit-breaker",
"contributors": [
"circuit-breaker",
"circuit-breaker-file",
"rate-limits-file",
"recommended-limits"
],
"inputs": {
"limits": "rate-limits-file",
"breaker": "circuit-breaker",
"breakerFile": "circuit-breaker-file",
"recommended": "recommended-limits"
}
}
],
"pow": [
{
"path": "security.pow.allowed",
"transform": "identity",
"role": "body",
"input": "pow",
"contributors": [
"pow"
]
}
],
"pow-trigger-percent": [
{
"path": "security.pow.triggerPercent",
"transform": "identity",
"role": "body",
"input": "pow-trigger-percent",
"contributors": [
"pow-trigger-percent"
]
}
],
"pow-starting-difficulty": [
{
"path": "security.pow.startDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-starting-difficulty",
"contributors": [
"pow-starting-difficulty"
]
}
],
"pow-maximum-difficulty": [
{
"path": "security.pow.maxDifficulty",
"transform": "identity",
"role": "body",
"input": "pow-maximum-difficulty",
"contributors": [
"pow-maximum-difficulty"
]
}
],
"smart-routing": [
{
"path": "smartRouting",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "smart-routing",
"contributors": [
"smart-routing"
]
}
],
"passkeys": [
{
"path": "passkeyEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "passkeys",
"contributors": [
"passkeys"
]
}
],
"cluster-gate": [
{
"path": "securityConfig.clusterGate.enabled",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-position": [
{
"path": "securityConfig.clusterGate.position",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-position",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-escalation": [
{
"path": "securityConfig.clusterGate.escalation",
"resource": "pipeline",
"transform": "identity",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-escalation",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"cluster-gate-home-countries": [
{
"path": "securityConfig.clusterGate.homeCountries",
"transform": "cluster-gate-home-countries",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "clusterGateIntent",
"transform": "present",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "cluster-gate-home-countries",
"contributors": [
"cluster-gate",
"cluster-gate-escalation",
"cluster-gate-home-countries",
"cluster-gate-position"
],
"inputs": {
"enabled": "cluster-gate",
"position": "cluster-gate-position",
"escalation": "cluster-gate-escalation",
"homeCountries": "cluster-gate-home-countries"
}
}
],
"otp-language": [
{
"path": "locale",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "otp-language",
"contributors": [
"otp-language"
]
}
],
"channel-choice": [
{
"path": "channelChoiceEnabled",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice",
"contributors": [
"channel-choice"
]
}
],
"channel-choice-mode": [
{
"path": "channelChoiceMode",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-mode",
"contributors": [
"channel-choice-mode"
]
}
],
"channel-choice-enforcement": [
{
"path": "channelChoiceEnforcement",
"transform": "identity",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "pipeline-read-revision",
"resource": "pipeline",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
},
{
"path": "expectedUpdatedAt",
"transform": "widget-read-revision",
"resource": "widget",
"role": "body",
"input": "channel-choice-enforcement",
"contributors": [
"channel-choice-enforcement"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
Protected linked-resource writes can be partial; honor PARTIAL_UPDATE details and inspect each named resource before retrying.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"_id": {
"type": "string"
},
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"updatedAt": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "A later linked write cannot be confirmed",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"code": {
"const": "PARTIAL_UPDATE"
},
"message": {
"type": "string"
},
"details": {
"type": "object",
"properties": {
"applied": {
"type": "array",
"items": {
"type": "object",
"properties": {
"resource": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [
"resource",
"id"
],
"additionalProperties": true
}
},
"notApplied": {
"type": "array",
"items": {
"type": "object",
"properties": {
"resource": {
"type": "string"
},
"id": {
"type": "string"
}
},
"required": [
"resource",
"id"
],
"additionalProperties": true
}
},
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
}
},
"required": [
"code",
"httpStatus"
],
"additionalProperties": true
}
},
"required": [
"applied",
"notApplied",
"error"
],
"additionalProperties": true
},
"hint": {
"type": "string"
}
},
"required": [
"code",
"message",
"details",
"hint"
],
"additionalProperties": true
},
"notes": [
"Inspect applied and notApplied resources. A rerun may repeat a write that completed without confirmation."
],
"title": "A later linked write cannot be confirmed"
},
{
"when": "--dry-run when linked settings require multiple resource writes",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
}
}
},
"required": [
"steps"
],
"additionalProperties": true
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"notes": [
"Linked planning returns status:\"success\" and data.steps. Planning reads may occur; none of the listed writes is executed."
],
"title": "--dry-run when linked settings require multiple resource writes"
},
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "PUT",
"url": "https://api.example.invalid/api/v1/widgets/0123456789abcdef01234567",
"body": {
"name": "Example widget"
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"_id": "0123456789abcdef01234567",
"name": "Example widget",
"status": "active",
"updatedAt": "2030-03-12T09:00:00.000Z"
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {
"WIDGET_CHANGED": "Run akedly widgets get <id> to inspect the current settings, then rerun the intended update. The command reads a fresh revision; do not reuse an old request body to overwrite another change.",
"WIDGET_POW_INVALID": "Use --pow on|off, --pow-trigger-percent 25..95, and --pow-starting-difficulty and --pow-maximum-difficulty 3..6. Starting difficulty must not exceed maximum difficulty. If changing only one difficulty, inspect the other with akedly widgets get <id>; an unset start is compared as 3 and an unset maximum as 5.",
"APPEARANCE_FIELD_UNKNOWN": "Remove or correct the appearance key named in details.field in --appearance-file. Supply the appearance object itself without a branding or appearance wrapper; the server decides which keys and values are supported. Unknown keys are refused rather than dropped."
},
"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 |
|---|---|
| WIDGET_CHANGED | Run akedly widgets get <id> to inspect the current settings, then rerun the intended update. The command reads a fresh revision; do not reuse an old request body to overwrite another change. |
| WIDGET_POW_INVALID | Use --pow on|off, --pow-trigger-percent 25..95, and --pow-starting-difficulty and --pow-maximum-difficulty 3..6. Starting difficulty must not exceed maximum difficulty. If changing only one difficulty, inspect the other with akedly widgets get <id>; an unset start is compared as 3 and an unset maximum as 5. |
| APPEARANCE_FIELD_UNKNOWN | Remove or correct the appearance key named in details.field in --appearance-file. Supply the appearance object itself without a branding or appearance wrapper; the server decides which keys and values are supported. Unknown keys are refused rather than dropped. |
widgets delete
Deactivate an embedded widget while preserving its stored identity and historical record.
When to use
- Use after removing its embed from applications and checking whether its one-time Secret is still needed.
How it works
- Requires confirmation and requests deletion; dry-run only previews.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
The widget record remains and status becomes inactive; the response data contains widgetId and status.
Next steps
- Remove the inactive widget from the host application; retain the ID for audit and historical reference.
Usage
akedly widgets delete <id>
Inspect this command offline before preparing an operation:
akedly describe "widgets delete" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <id> | ^[a-fA-F0-9]{24}$ | Use the 24-hex Mongo widget _id. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--yes | boolean switch | — | Confirm this action where consent is required. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--help | boolean switch | — | Show offline command help. |
--output | enum | json | human | Select JSON or human output. |
--fields | dot-paths | — | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: widgets:delete.
Server request: DELETE /widgets/:id.
Consent
--yes. --dry-run bypasses confirmation where supported. Destructive: true. Spends money: false. Real send: false.
Retry
write: Non-idempotent writes are not automatically retried. guarded-write: when an update carries the revision read from the server, its write is not automatically retried after network errors or HTTP 429/502/503/504. It may already be saved; inspect the resource before rerunning. A rerun reads current settings again. The explicit CLI_RATE_LIMITED refusal exception below still applies. recommendation-read: the recommendation request made while planning --recommended-limits is not automatically retried after network errors or HTTP 429/502/503/504. If planning fails, no update is sent; rerun the command to request a new recommendation. The explicit CLI_RATE_LIMITED refusal exception below still applies. This label does not change the retry policy of the standalone rate-recommendations command.
Output
Output: data. API output is pass-through server data unless the command details describe a CLI shape. Local commands, composites and hand-offs use their documented CLI shapes.
Full command details
Soft-delete a widget using its 24-hex Mongo _id; --yes required. Restore with widgets update <id> --status active --yes.
Worked examples
Examples are synthetic illustrations, never live account records. Replace resource IDs with your own authorized resources. Secret values are omitted here; redacted examples illustrate the shape and are not schema-validation fixtures.
Synthetic widgets delete workflow
Remove a widget that should no longer be embedded or accept verification attempts.
akedly widgets delete 0123456789abcdef01234567 --yes
Illustrative response (sensitive values omitted):
{
"status": "success",
"message": "Widget deleted successfully (soft delete)",
"data": {
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "inactive"
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets delete <id> --yes
Input and file contracts
The contract below describes typed arguments, file formats, validation boundaries and API mappings. Boolean switches take no value; numeric and comma-separated values are each passed as one argument. Read prerequisites and full command details for state-dependent requirements.
{
"flagTokenEncoding": {
"booleans": "A boolean schema describes switch presence: write --flag with no value.",
"valuedFlags": "A string schema describes the single argv token after --flag or after --flag=; numeric token schemas parse as decimal strings and validation.parsedValueSchema documents the converted number.",
"csvFlags": "A list schema describes one comma-separated argv token, not multiple shell arguments.",
"projection": "--fields is one comma-separated argv token of safe dot paths and changes returned output only."
},
"positionals": [
{
"name": "id",
"type": "string",
"required": true,
"pattern": "^[a-fA-F0-9]{24}$",
"validation": "Use the 24-hex Mongo widget _id.",
"mapsToRouteParameter": "id",
"routeParameterValidation": "A route parameter cannot be . or .., including percent-encoded forms."
}
],
"flags": [
{
"name": "help",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "output",
"schema": {
"type": "string",
"enum": [
"json",
"human"
]
},
"required": false
},
{
"name": "fields",
"schema": {
"type": "string",
"pattern": "^(?!.*(?:^|[.,])(?:__proto__|constructor|prototype)(?:[.,]|$))(?!,)(?!.*,,)(?!.*,$)[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*(?:,[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*)*$"
},
"required": false,
"validation": {
"syntax": "Comma-separated safe dot paths; reserved prototype keys are refused."
}
},
{
"name": "ksa",
"schema": {
"type": "boolean"
},
"required": false
},
{
"name": "yes",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "yes",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "yes",
"inputRole": "local-control",
"transform": "identity",
"purpose": "yes",
"contributors": [
"yes"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "DELETE",
"route": "/widgets/:id",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"widgetId": {
"type": "string",
"pattern": "^widget_[a-f0-9]{32}$"
},
"status": {
"const": "inactive"
}
},
"required": [
"widgetId",
"status"
],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"message",
"data"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "DELETE",
"url": "https://api.example.invalid/api/v1/widgets/0123456789abcdef01234567"
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"message": "Widget deleted successfully (soft delete)",
"data": {
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "inactive"
}
}
}
]
{
"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
}
}
widgets pow-meta
Read the server’s allowed proof-of-work parameters for widgets before tuning widget PoW.
When to use
- Use when configuring a widget’s PoW difficulty or validating a policy refusal.
How it works
- Reads the widget PoW policy metadata.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Policy settings returned by the server.
Next steps
- Select values within the returned policy bounds.
Usage
akedly widgets pow-meta
Inspect this command offline before preparing an operation:
akedly describe "widgets pow-meta" --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: widgets:read.
Server request: GET /widgets/pow-meta.
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 proof-of-work policy metadata. Read-only logins are allowed.
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 widgets pow-meta workflow
Read the server’s allowed proof-of-work parameters for widgets before tuning widget PoW.
akedly widgets pow-meta
Illustrative response (sensitive values omitted):
{
"levels": [
{
"difficulty": 3,
"label": "Easy",
"estimateMobile": "instant (<0.2s)",
"estimateDesktop": "<0.1s"
},
{
"difficulty": 4,
"label": "Light",
"estimateMobile": "~0.5–3s",
"estimateDesktop": "~0.2–1s"
},
{
"difficulty": 5,
"label": "Medium",
"estimateMobile": "~7s (up to ~45s on slower phones)",
"estimateDesktop": "~2–10s"
},
{
"difficulty": 6,
"label": "Heavy",
"estimateMobile": "~40s–4min",
"estimateDesktop": "~30–90s",
"warn": "Not recommended — may exceed the verification window on mobile."
}
],
"hardMaxDifficulty": 5
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets pow-meta
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": "/widgets/pow-meta",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"levels": {
"type": "array",
"items": {
"type": "object",
"properties": {
"difficulty": {
"type": "integer"
},
"label": {
"type": "string"
},
"estimateMobile": {
"type": "string"
},
"estimateDesktop": {
"type": "string"
},
"warn": {
"type": "string"
}
},
"required": [
"difficulty",
"label",
"estimateMobile",
"estimateDesktop"
],
"additionalProperties": true
}
},
"hardMaxDifficulty": {
"type": "integer"
}
},
"required": [
"levels",
"hardMaxDifficulty"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"levels": [
{
"difficulty": 3,
"label": "Easy",
"estimateMobile": "instant (<0.2s)",
"estimateDesktop": "<0.1s"
},
{
"difficulty": 4,
"label": "Light",
"estimateMobile": "~0.5–3s",
"estimateDesktop": "~0.2–1s"
},
{
"difficulty": 5,
"label": "Medium",
"estimateMobile": "~7s (up to ~45s on slower phones)",
"estimateDesktop": "~2–10s"
},
{
"difficulty": 6,
"label": "Heavy",
"estimateMobile": "~40s–4min",
"estimateDesktop": "~30–90s",
"warn": "Not recommended — may exceed the verification window on mobile."
}
],
"hardMaxDifficulty": 5
}
}
]
{
"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
}
}
widgets upload-logo
Upload a local widget logo image and obtain a hosted URL for widget branding.
When to use
- Use when branding needs a logo URL and the source image is available locally.
How it works
- Reads and validates the local image then uploads it to the configured image service; this is an external write and dry-run does not upload.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
An image-provider-hosted URL and upload dimensions; the command does not itself save the URL to a widget.
Next steps
- Pass the returned URL to widgets update to save the branding change.
Usage
akedly widgets upload-logo
Inspect this command offline before preparing an operation:
akedly describe "widgets 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: widgets:update.
Server request: POST /cloudinary/uploadWidgetLogo.
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 one local PNG, JPEG, GIF or WebP regular file, at most 3 MiB (no SVG). Requires Full access. Then store the returned URL with akedly widgets update <id> --logo <url>.
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 widgets upload-logo workflow
Upload a local widget logo image and obtain a hosted URL for widget branding.
akedly widgets upload-logo --file ./logo.png
Input file: ./logo.png (base64).
iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAEUlEQVR4nGPQyNuCFTEMLQkAGahSgZLbNF8AAAAASUVORK5CYII=
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"url": "https://images.example.invalid/widget-logo.png"
},
"message": "Logo uploaded successfully"
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
The synthetic PNG is 8×8 pixels, meeting the logo-upload minimum dimension. Uploading it contacts the image provider; this example is documentation only.
Additional command forms
akedly widgets upload-logo --file <image>
akedly widgets update <id> --logo <returned-url>
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/uploadWidgetLogo",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"url": {
"type": "string"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data",
"message"
],
"additionalProperties": true
},
"variants": [
{
"when": "--dry-run on a single request (no linked-resource fan-out)",
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"method": {
"type": "string"
},
"url": {
"type": "string"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object",
"additionalProperties": true
}
},
"required": [
"method",
"url"
],
"additionalProperties": true
},
"example": {
"method": "POST",
"url": "https://api.example.invalid/api/v1/cloudinary/uploadWidgetLogo",
"body": {
"fileURL": "data:image/png;base64,[elided 74 bytes]"
}
},
"notes": [
"The preview is a raw method/url/body DTO, not a success/data API envelope. Planning reads can still occur for commands that need current settings or revisions."
],
"title": "--dry-run on a single request (no linked-resource fan-out)"
}
],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"url": "https://images.example.invalid/widget-logo.png"
},
"message": "Logo uploaded 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
}
}
widgets rate-recommendations
Estimate widget rate limits and circuit-breaker values from observed traffic.
When to use
- Use before applying widget protections; enough traffic is required for ready recommendations.
How it works
- Requests account-level recommendation analysis for the selected period or date range; no widget settings are changed.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Analysis readiness/confidence and recommended setting fields.
Next steps
- Apply reviewed values with widgets update; do not apply when traffic confidence is low.
Usage
akedly widgets rate-recommendations
Inspect this command offline before preparing an operation:
akedly describe "widgets rate-recommendations" --output json
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--period | enum | last-month | last-quarter | last-6-months | Command-specific option; see the full command details below. Manifest body field: period. |
--start-date | text | — | Command-specific option; see the full command details below. Manifest body field: customDateRange.sDate. |
--end-date | text | — | Command-specific option; see the full command details below. Manifest body field: customDateRange.eDate. |
--dry-run | boolean switch | — | Preview without sending the write or creating output files; planning reads may still occur. Bypass consent. |
--help | boolean switch | — | Show offline command help. |
--output | enum | json | human | Select JSON or human output. |
--fields | dot-paths | — | Select comma-separated safe dot paths; apply to each element of a root array. |
Authentication, permission and guards
Auth: login. Permission: widgets:update.
Server request: POST /widget-rate-recommendations.
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 widget rate recommendations. Requires Full access; dates use server-local-day semantics; default period last-month.
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 widgets rate-recommendations workflow
Estimate widget rate limits and circuit-breaker values from observed traffic.
akedly widgets rate-recommendations --period last-month
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"applyReady": {
"enoughTraffic": true,
"confidence": "high",
"analysedRequests": 250,
"settings": {
"rateLimiting": {
"otpRequests": {
"perWidget": {
"perMinute": 30,
"perHour": 600,
"perDay": 3000
}
}
}
}
}
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets rate-recommendations
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": "period",
"schema": {
"type": "string",
"enum": [
"last-month",
"last-quarter",
"last-6-months"
]
},
"required": false,
"wire": [
{
"path": "period",
"transform": "identity",
"role": "body",
"input": "period",
"contributors": [
"period"
]
}
],
"valueTranslations": {
"last-month": "last_month",
"last-quarter": "last_quarter",
"last-6-months": "last_6_months"
}
},
{
"name": "start-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "customDateRange.sDate",
"transform": "identity",
"role": "body",
"input": "start-date",
"contributors": [
"start-date"
]
}
]
},
{
"name": "end-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "customDateRange.eDate",
"transform": "identity",
"role": "body",
"input": "end-date",
"contributors": [
"end-date"
]
}
]
},
{
"name": "dry-run",
"schema": {
"type": "boolean"
},
"required": false,
"input": {
"role": "local-control",
"purpose": "dry-run",
"validationBoundary": "No separately curated semantic file schema is available; use the local reader contract and command help to understand additional handler or server validation."
},
"wire": [
{
"role": "local",
"input": "dry-run",
"inputRole": "local-control",
"transform": "identity",
"purpose": "dry-run",
"contributors": [
"dry-run"
]
}
]
}
],
"files": [],
"wire": {
"method": "POST",
"route": "/widget-rate-recommendations",
"query": {},
"body": {
"period": [
{
"path": "period",
"transform": "identity",
"role": "body",
"input": "period",
"contributors": [
"period"
]
}
],
"start-date": [
{
"path": "customDateRange.sDate",
"transform": "identity",
"role": "body",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"end-date": [
{
"path": "customDateRange.eDate",
"transform": "identity",
"role": "body",
"input": "end-date",
"contributors": [
"end-date"
]
}
]
},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"applyReady": {
"type": "object"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"applyReady": {
"enoughTraffic": true,
"confidence": "high",
"analysedRequests": 250,
"settings": {
"rateLimiting": {
"otpRequests": {
"perWidget": {
"perMinute": 30,
"perHour": 600,
"perDay": 3000
}
}
}
}
}
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
widgets analytics
Inspect verification and usage analytics for one public widget identifier.
When to use
- Use to understand traffic and outcomes for a widget over a bounded date range.
How it works
- Reads analytics by public widgetId (widget_<32 lowercase hex>); date bounds are optional but must be supplied together.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Analytics summary and time-series fields returned by the API.
Next steps
- Use the Mongo _id for resource edits; analytics requires the public widgetId.
Usage
akedly widgets analytics <widgetId>
Inspect this command offline before preparing an operation:
akedly describe "widgets analytics" --output json
| Positional | Grammar | Usage guidance |
|---|---|---|
| <widgetId> | ^widget_[a-f0-9]{32}$ | Use the public widget_<32 hex> widgetId for analytics. |
Flags
| Flag | Type | Enum / range | Description |
|---|---|---|---|
--ksa | boolean switch | — | Select the KSA deployment. |
--start-date | text | — | Command-specific option; see the full command details below. Query parameter: startDate. |
--end-date | text | — | Command-specific option; see the full command details below. Query parameter: endDate. |
--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: widgets:read.
Server request: GET /widget-analytics/:widgetId/analytics.
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 analytics using public widget_<32 hex> widgetId, not Mongo _id. Supply both date bounds or neither (neither date = last 24 hours); maximum 30 elapsed days. Read-only allowed.
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 widgets analytics workflow
Inspect verification and usage analytics for one public widget identifier.
akedly widgets analytics widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": {
"widget": {
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "Example widget",
"status": "active"
},
"currentStatus": {
"online": "idle",
"lastAttemptAt": null
},
"health": {
"status": "healthy",
"message": "No recent failures",
"captchaSuccessRate": 100
},
"concurrentUsers": {
"current": 0,
"last1Minute": 0,
"last5Minutes": 0,
"last15Minutes": 0,
"last1Hour": 0,
"last24Hours": 0
},
"activityMetrics": {
"totalAttempts": {
"last24Hours": 0
},
"successRates": {
"overallVerificationRate": 100
},
"statusBreakdown": {},
"avgVerificationTime": 0,
"hourlySuccessRates": []
},
"failureAnalysis": {
"topFailureReasons": [],
"failureTypeBreakdown": []
},
"channelBreakdown": [],
"resendMetrics": {},
"peakConcurrentUsers": 0
}
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets analytics <widgetId>
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": "widgetId",
"type": "string",
"required": true,
"pattern": "^widget_[a-f0-9]{32}$",
"validation": "Use the public widget_<32 hex> widgetId for analytics.",
"mapsToRouteParameter": "widgetId",
"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": "start-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "startDate",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
]
},
{
"name": "end-date",
"schema": {
"type": "string"
},
"required": false,
"wire": [
{
"path": "endDate",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
]
}
],
"files": [],
"wire": {
"method": "GET",
"route": "/widget-analytics/:widgetId/analytics",
"query": {
"start-date": [
{
"path": "startDate",
"transform": "identity",
"role": "query",
"input": "start-date",
"contributors": [
"start-date"
]
}
],
"end-date": [
{
"path": "endDate",
"transform": "identity",
"role": "query",
"input": "end-date",
"contributors": [
"end-date"
]
}
]
},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "object",
"properties": {
"widgetId": {
"type": "string"
},
"totalRequests": {
"type": "integer"
},
"successfulVerifications": {
"type": "integer"
},
"series": {
"type": "array"
}
},
"required": [],
"additionalProperties": true
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": {
"widget": {
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "Example widget",
"status": "active"
},
"currentStatus": {
"online": "idle",
"lastAttemptAt": null
},
"health": {
"status": "healthy",
"message": "No recent failures",
"captchaSuccessRate": 100
},
"concurrentUsers": {
"current": 0,
"last1Minute": 0,
"last5Minutes": 0,
"last15Minutes": 0,
"last1Hour": 0,
"last24Hours": 0
},
"activityMetrics": {
"totalAttempts": {
"last24Hours": 0
},
"successRates": {
"overallVerificationRate": 100
},
"statusBreakdown": {},
"avgVerificationTime": 0,
"hourlySuccessRates": []
},
"failureAnalysis": {
"topFailureReasons": [],
"failureTypeBreakdown": []
},
"channelBreakdown": [],
"resendMetrics": {},
"peakConcurrentUsers": 0
}
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
widgets overview
Review account-wide widget analytics at a glance.
When to use
- Use for a high-level widget usage snapshot before drilling into an individual widget.
How it works
- Reads the widget analytics overview without changing configuration.
Prerequisites
- A CLI login with permission for this account and resource.
Expected outcome
Overview metrics returned by the server.
Next steps
- Open widgets analytics for a specific public widgetId.
Usage
akedly widgets overview
Inspect this command offline before preparing an operation:
akedly describe "widgets overview" --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: widgets:read.
Server request: GET /widget-analytics/overview.
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 last-24-hours overview of your active and inactive widgets; the window is fixed. Read-only logins are allowed.
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 widgets overview workflow
Review account-wide widget analytics at a glance.
akedly widgets overview
Illustrative response (sensitive values omitted):
{
"status": "success",
"data": [
{
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "Example widget",
"status": "active",
"currentStatus": {
"online": "idle",
"lastAttemptAt": null
},
"health": {
"status": "healthy",
"message": "No recent failures",
"captchaSuccessRate": 100
},
"concurrentUsers": {
"current": 0,
"last24Hours": 0
},
"last24HourMetrics": {
"totalAttempts": 0,
"verificationRate": 100,
"statusBreakdown": {},
"avgVerificationTime": 0
}
}
]
}
Assumes an authorized account and the referenced resource exists where required. All values and IDs are synthetic; this example is documentation only.
Additional command forms
akedly widgets overview
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": "/widget-analytics/overview",
"query": {},
"body": {},
"ownerParams": []
}
}
Response contract
The CLI preserves the server success envelope and passes through resource-specific fields; additional data fields are server extensions, not fields this command promises to synthesize.
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"status": {
"const": "success"
},
"data": {
"type": "array",
"properties": {
"totalWidgets": {
"type": "integer"
},
"activeWidgets": {
"type": "integer"
},
"totalRequests": {
"type": "integer"
}
},
"required": [],
"additionalProperties": true,
"items": {
"type": "object",
"properties": {
"widgetId": {
"type": "string"
},
"name": {
"type": "string"
},
"status": {
"type": "string"
},
"currentStatus": {
"type": "object",
"additionalProperties": true
},
"health": {
"type": "object",
"additionalProperties": true
},
"concurrentUsers": {
"type": "object",
"additionalProperties": true
},
"last24HourMetrics": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": true
}
},
"message": {
"type": "string"
}
},
"required": [
"status",
"data"
],
"additionalProperties": true
},
"variants": [],
"fieldSelection": {
"flag": "fields",
"behavior": "Selects comma-separated dot paths from the response root; for root arrays, paths apply to each element. Missing paths are omitted.",
"sensitiveData": null
}
}
[
{
"title": "Synthetic successful response",
"value": {
"status": "success",
"data": [
{
"widgetId": "widget_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"name": "Example widget",
"status": "active",
"currentStatus": {
"online": "idle",
"lastAttemptAt": null
},
"health": {
"status": "healthy",
"message": "No recent failures",
"captchaSuccessRate": 100
},
"concurrentUsers": {
"current": 0,
"last24Hours": 0
},
"last24HourMetrics": {
"totalAttempts": 0,
"verificationRate": 100,
"statusBreakdown": {},
"avgVerificationTime": 0
}
}
]
}
}
]
{
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [
"status",
"code",
"message",
"httpStatus",
"hint"
],
"properties": {
"status": {
"const": "error"
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"httpStatus": {
"type": [
"integer",
"null"
]
},
"details": {
"anyOf": [
{
"type": "object",
"minProperties": 1
},
{
"type": "array",
"minItems": 1
}
]
},
"hint": {
"type": "string"
}
},
"additionalProperties": false
},
"globalHints": {
"TOTP_DELETE_RECONCILIATION_PENDING": "Any Pending request blocks deletion, including one just opened; Failed requests can block too. Ordinary unverified, unbilled requests expire about five minutes after creation and normally become Expired on the next five-minute reconciliation run, within about ten minutes of creation; job delays or billing/counter recovery can extend that wait. Check the stored status in akedly totp history list; computed isExpired alone does not unblock deletion. Ask Akedly support if a request stays Pending or Failed beyond that normal window, or is Verified and still blocks.",
"CLI_SECRET_ALREADY_DELIVERED": "Run akedly login again.",
"CLI_LOGIN_INVALID": "Run akedly login.",
"PERMISSION_DENIED": "If details.permission names a permission, ask an organization admin to grant it to your role. Otherwise read the server message: an organization admin cannot clear refusals such as an inactive or unverified account, an Akedly-managed global role, a reported-invitation pause or missing enterprise entitlement; follow the message or contact Akedly support. If the message says BYO-SMS is not enabled for this account, the account needs BYO-SMS entitlement and granular pricing; akedly whoami shows user.byoSmsEntitled and user.pricingModel, and only Akedly can change either.",
"ORG_ADMIN_REQUIRED": "If the message asks for an active organization session, run akedly login and approve it with the intended organization active in the dashboard. If it says organization admin access is required, ask an organization admin for access or to run the command. Role changes can take up to 60 seconds to apply.",
"OWNER_REQUIRED": "Only the account owner can do this; an organization admin is not necessarily the owner. Ask the owner to run it from their own login. For seats, organization admins can read akedly seats visibility; akedly seats status is owner-only.",
"LOGIN_REQUIRED": "Run akedly login. If you supplied a credential, check --ksa or AKEDLY_REGION.",
"CLI_DISABLED": "Customer CLI access is turned off on this Akedly server. Try again later or contact Akedly support.",
"CLI_IDENTITY_UNAVAILABLE": "Akedly could not confirm your login right now. Wait a minute and try again.",
"CLI_LOGIN_INDEX_UNAVAILABLE": "CLI login is temporarily unavailable on this server. Try again later or contact Akedly support.",
"WABA_PHONE_CHECK_UNAVAILABLE": "Meta did not confirm the number. Wait a few minutes, then run the command again.",
"CLI_UPGRADE_REQUIRED": "Install the latest @akedly/cli.",
"NOT_AVAILABLE_ON_SERVER": "Check the selected region or ask an operator to deploy CLI support.",
"CLI_RATE_LIMITED": "Wait before trying again.",
"CLI_LOGIN_DENIED": "Run akedly login again and approve the new code.",
"CLI_LOGIN_REQUEST_EXPIRED": "Run akedly login again.",
"LOGIN_TIMEOUT": "Run akedly login again and approve before the timeout.",
"CLI_READ_ONLY": "This login is read-only. Run akedly login and choose Full access.",
"CLI_LOGIN_LIMIT_REACHED": "Run akedly cli-logins list, then akedly cli-logins end <id>",
"CLI_LOGIN_NOT_FOUND": "Run akedly cli-logins list and check the login id.",
"CLI_LOGIN_OWNER_PROTECTED": "Ask the account owner to end this login.",
"IMPERSONATION_BLOCKED": "This login was approved by Akedly staff and can't do this. Run akedly login and approve it in your own dashboard session.",
"SELF_IDENTITY_REQUIRED": "Personal settings need your own login. Run akedly login and approve it in your own dashboard session.",
"MEMBER_NOT_FOUND": "Check the email with akedly team members list; a pending invitation is not a member yet.",
"MEMBER_AMBIGUOUS": "Pass the memberId from akedly team members list instead of the email.",
"API_KEY_ACTIVE_CAP_REACHED": "Revoke an unused key before creating another.",
"API_KEY_ISSUANCE_DISABLED": "Ask an operator to enable API-key issuance.",
"API_KEY_VERSION_CONFLICT": "List the keys and retry the edit with the current version.",
"API_KEY_BUDGET_CONFIRMATION_REQUIRED": "Review the reduction and pass --confirm-budget-reduction.",
"API_KEY_ISSUE_RECOVERY_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_TRANSITION_UNAVAILABLE": "Revoke the key and create a new one.",
"API_KEY_ISSUE_IDEMPOTENCY_CONFLICT": "Use a new idempotency key for different issue details.",
"API_KEY_SCOPE_NOT_ACTIVE": "Ask an operator to enable API-key scope editing.",
"API_KEY_BUDGET_NOT_ACTIVE": "Ask an operator to enable API-key budget editing.",
"API_KEY_NOT_FOUND": "Run akedly api-keys list and check the id.",
"API_KEY_BUDGET_RACE": "List the keys and retry.",
"V1_CREATION_NOT_ENTITLED": "V1 pipelines are not available to this account. Omit --api-version to create a V1.2 pipeline.",
"SECRET_SUFFIX_REQUIRED": "Pass exactly one of --secret-file <saved path> or --secret-suffix <last 4 characters>.",
"SECRET_SUFFIX_MISMATCH": "Reveal the current secret to --secret-file <new path> with akedly pipelines webhook-secret reveal <id> for webhook secrets, or akedly utilities pipelines signing-secret reveal <id> for utility request-signing secrets, then acknowledge with exactly one of --secret-file <that path> or --secret-suffix <last 4 characters>.",
"CHANNEL_CHOICE_NOT_AVAILABLE": "Nothing in this request was saved. Each value in details.blockers names what is missing: not_entitled means Channel Choice isn't enabled on the account yet; legacy_pricing means the account needs granular pricing; not_pay_per_message means the pipeline is not billed per message, because it is billed per successful verification or the account has no billing plan yet; account_excluded and global_switch_off mean Channel Choice isn't offered to this account right now. Only Akedly can change these, so contact Akedly. utility_pipeline means utility pipelines have no Channel Choice. Resend any other changes without the Channel Choice flags.",
"VERIFICATION_METHOD_UNAVAILABLE": "Nothing was saved. details.method is the word you typed, or the method's id if the method became unavailable after the CLI checked; running the command again rechecks the list and names the word. Remove that method from --verification-methods and retry, or contact Akedly if you expected it to be available.",
"VERIFICATION_METHODS_REQUIRED": "A pipeline that has verification methods must keep at least one. Send the methods to keep instead of an empty list, for example akedly pipelines update <id> --verification-methods whatsapp,sms --yes.",
"SETTING_NOT_FOR_THIS_PIPELINE": "Nothing in this request was saved. details.setting names the setting and details.useInstead what to use instead. For rate limits and the circuit breaker, reason api-version means this is a V1 or V2 pipeline, where they belong to a widget instead, so useInstead is widgets update. A widget must be attached to this pipeline first. Find attached widgets with akedly widgets list-filtered --pipeline-id <pipelineId>, then inspect the intended widget with akedly widgets get <widgetId>, using its 24-hex _id. This listing excludes inactive widgets by default. Also run akedly widgets list-filtered --pipeline-id <pipelineId> --status inactive before concluding that no widget is attached. Change its limits with akedly widgets update <widgetId> --rate-limits-file <file> --yes; use the widget file shape, not the pipeline file shape. Set its breaker with akedly widgets update <widgetId> --circuit-breaker on --yes (or off), and use --circuit-breaker-file <file> for widget thresholds and suspension durations; pipeline --circuit-breaker-sensitivity does not apply to widgets. If no widget is attached, create one with akedly widgets create --name <name> --pipeline-id <pipelineId> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <pipelineId> --yes. V2 attachment requires both callback URLs; V1 attachment requires entitlement. pipelines update now routes shared rate-limit, breaker and PoW flags to every attached widget on V1/V2. V1.2-only controls remain refused on V1/V2. Use the appropriate file shape for the resulting API version. For the Channel Choice widget experience, reason api-version means it needs a V2 pipeline, so create one with akedly pipelines create --name <name> --api-version v2 --redirect-url <url> --webhook-url <url> --secret-file <new path>, then attach a widget to it; reason no-widget means it needs a widget attached to this V2 pipeline, so create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, or attach an existing one with akedly widgets update <widgetId> --pipeline-id <id> --yes, then retry. Resend any other changes without that flag.",
"PIPELINE_CHANGED": "Another change landed first, so nothing was saved. Run the same command again; if you changed rate limits or destination countries, it reads the pipeline again first and merges your change onto the new version. Run akedly pipelines get <id> to see what changed.",
"RATE_LIMIT_INVALID": "Check details.field and any details.errors. Use whole numbers above zero in the expected JSON object shape. Pipeline --rate-limits-file uses perPhoneNumber, perIpAddress and perPipeline; widget --rate-limits-file uses widgetAttempts and otpRequests, each with perPhoneNumber, perDevice and perWidget. Each takes perMinute, perHour and perDay. Widget --circuit-breaker-file uses floodThresholds and suspensionSeconds; durations are whole seconds. Correct the named field and rerun the command.",
"EXPECTED_UPDATED_AT_INVALID": "The CLI reads the revision automatically. Run the command again to read the current settings; if this continues, check that the CLI and server are up to date and contact Akedly support. Do not invent a revision or remove the conflict check.",
"DESTINATION_COUNTRIES_MODE_REQUIRED": "Nothing in this request was saved. A changed country list under mode all would be discarded: pass --destination-countries-mode only-selected or all-except-selected with --destination-countries, or leave the list out to keep every country open. --destination-countries-mode all on its own clears the list.",
"NOT_ENOUGH_TRAFFIC": "Keep the current limits for now. Recommended limits need at least 100 analysed requests and better than low confidence; try again after more real traffic or with a longer --period such as last-quarter, or set limits yourself with --rate-limits-file.",
"INVALID_COUNTRY_POLICY": "Nothing in this request was saved. Use two-letter country codes such as EG,SA with --destination-countries and a mode of all, only-selected or all-except-selected; the message names the problem.",
"OTP_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"TURNSTILE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_PIPELINE_CHANGED": "Another change landed first. Run akedly pipelines get <id> and retry.",
"CLUSTER_GATE_NOT_AVAILABLE": "Nothing in this request was saved. Act on details.reason: not-offered means Cluster Gate is not offered for this pipeline right now, for example on a utility pipeline or in the KSA region; attaching a widget will not help on a V1.2 pipeline, because V1.2 pipelines cannot be attached to widgets; drop the Cluster Gate flags and resend any other changes; no-widget means Cluster Gate applies here only through a widget and none is attached, so attach an existing widget with akedly widgets update <widgetId> --pipeline-id <id> --yes, or create one with akedly widgets create --name <name> --pipeline-id <id> --secret-file <new path>, then retry; the pipeline must first be active, with both callback URLs and a verification method, and WIDGET_PIPELINE_NOT_READY says what is missing; on-create comes only from raw API requests that set Cluster Gate while creating a pipeline: create the pipeline first, then set Cluster Gate with akedly pipelines update <id> --cluster-gate on --cluster-gate-position balanced --yes; check-failed means availability could not be checked, so retry later; intent-missing comes only from raw API requests that omit clusterGateIntent: true.",
"SECRET_ALREADY_ACKNOWLEDGED": "The secret was already acknowledged and cannot be shown again. Use the saved copy from pipeline creation (webhook secrets) or from reveal, for example the file written with --secret-file. Only if that copy is lost, use akedly pipelines webhook-secret regenerate <id> --yes --secret-file <new path> for webhook secrets, or akedly utilities pipelines signing-secret regenerate <id> --yes --secret-file <new path> for utility request-signing secrets. Regenerating stops the current secret immediately; deploy the new secret before using signed requests or accepting signed webhooks.",
"WIDGET_PIPELINE_NOT_READY": "Fix each item in details.missing with akedly pipelines update <pipelineId> --status active --redirect-url <url> --webhook-url <url> --verification-methods whatsapp,sms,email --yes, passing only the flags for the missing items, then retry.",
"WIDGET_IDENTITY_INDEX_UNAVAILABLE": "Widget identity indexes are unavailable. Retry later or contact Akedly support.",
"INCOMPLETE_DATE_RANGE": "Supply both --start-date and --end-date, or neither.",
"DATE_RANGE_TOO_LARGE": "Choose a date range of at most 30 days, counted as the command help describes.",
"INVALID_DATE_RANGE": "Check both date bounds and their order.",
"INVALID_DATE": "Use valid ISO dates for the requested range.",
"NEEDS_BROWSER": "Turn Turnstile off for the pipeline in the dashboard, or run akedly pipelines dev-mode set <pipelineId> --dev-mode on --bypass-turnstile on --bypass-pow on --yes.",
"SETUP_REQUIRED": "Finish phone setup on the Personal Notifications page of your Akedly dashboard, then retry.",
"INVALID_API_KEY": "Check the API key: akedly api-keys list, or create one with akedly api-keys create --label <name> --save --yes.",
"API_KEY_SCOPE_DENIED": "This API key is scoped to other pipelines; use a key that includes this pipeline.",
"MAX_ATTEMPTS_EXCEEDED": "Too many wrong codes; send a new OTP.",
"ALREADY_VERIFIED": "This request is already verified; check it with akedly transactions status.",
"INVALID_OTP": "Check the code and retry; attempts are limited.",
"PIPELINE_INACTIVE": "Run akedly pipelines update <pipelineId> --status active --yes, then retry.",
"POW_CHALLENGE_EXPIRED": "Run the command again; it fetches a fresh challenge.",
"POW_CHALLENGE_REPLAYED": "Run the command again; it fetches a fresh challenge.",
"POW_DIFFICULTY_STALE": "Run the command again; it fetches a fresh challenge.",
"STATUS_LOOKUP_RATE_LIMITED": "Wait for the retryAfter seconds in details, then retry; each lookup counts against the 300 ids per minute limit.",
"TRANSACTION_EXPIRED": "This code or request has expired; send a new one.",
"USE_V1_2_ENDPOINT": "This pipeline is V1.2. Use akedly otp v1.2 send.",
"SIGNATURE_EXPIRED": "This machine's clock differs from the server's by more than 60 seconds. Fix the clock and run the command again; it signs a fresh request each time.",
"WIDGET_USER_MISMATCH": "This API key belongs to a different account than the widget. Use a key from the widget owner's account.",
"WEBHOOK_SIGNATURE_INVALID": "Use the pipeline's current whsec_ secret (regenerating invalidates the old one), the exact raw body bytes the server received (no re-serialising, no trailing newline), and the svix-id, svix-timestamp and svix-signature headers.",
"WAIT_EXPIRED": "The request expired before it was verified; it cannot complete. Start a new one (otp v2 attempt, otp v1.2 send or otp v1 create).",
"WAIT_FAILED": "The verification failed and cannot complete; start a new one.",
"WAIT_TIMEOUT": "The wait ended; the verification may still complete. Check it with akedly transactions status. Resume with akedly transactions status --attempt-id <id> --wait (or --transaction-id <id> --wait).",
"WEBHOOK_TIMESTAMP_STALE": "The webhook timestamp is outside the allowed window. Check this machine's clock, or pass --at <unix seconds> to check an older delivery, or --tolerance-seconds <seconds>.",
"WEBHOOK_HEADERS_MISSING": "Pass a headers JSON file with the svix-id, svix-timestamp and svix-signature headers exactly as received.",
"NO_USABLE_API_KEY": "This account has no usable API key for this pipeline. Create one with akedly api-keys create --label <name> --save --yes; a key scoped to selected pipelines must include this pipeline.",
"INVALID_API_VERSION": "This command does not support this pipeline's API version. Check it with akedly pipelines get <id> and use the matching command for that version.",
"DOCTOR_FAILED": "Fix the failed checks in details.checks, then run akedly doctor again.",
"PAGINATION_LIMIT": "Narrow the filters, for example a date range or status, or raise --limit (maximum 100), then run again.",
"PAGINATION_STALLED": "The list changed while it was being read, or the server stopped advancing. Run the command again.",
"REPORT_NOT_FOUND": "Run akedly reports list and check the reportId.",
"DAILY_LIMIT_REACHED": "Wait until an earlier successful, sent or in-flight report generation leaves the rolling 24-hour window; for example, a generation at 14:00 stops counting after 14:00 the next day. This limit covers the whole account, including dashboard and teammate generations.",
"REPORT_IN_PROGRESS": "A report is already generating for this account; dashboard and teammate generations share the same slot. Run akedly reports list before trying again.",
"TEMPLATE_DUPLICATE": "Run akedly whatsapp otp-templates list; recover missing languages with update --add-languages instead of adding the same template again.",
"TEMPLATE_LIMIT_REACHED": "Archive an unused template with akedly whatsapp otp-templates delete <id> --yes before adding another; the limit is 20 per WABA.",
"TEMPLATE_PROVISIONING_FAILED": "The template or changes are saved. Run akedly whatsapp otp-templates list, then update <id> --add-languages <failed languages> rather than creating again.",
"TEMPLATE_WABA_REQUIRED": "Connect an active own WhatsApp Business Account in the dashboard before managing OTP templates; check akedly whatsapp status.",
"TEMPLATE_WABA_CHANGED": "This template belongs to the previous WABA. Add it again for the currently connected account.",
"COUNTRY_CATALOG_UNAVAILABLE": "The country catalog is temporarily unavailable. Try again later.",
"NOT_A_UTILITY_PIPELINE": "Choose an owned utility pipeline from akedly pipelines list, or create one with akedly pipelines create --product utility --name <name>.",
"TEST_RATE_LIMITED": "Wait for Retry-After before testing again; account, recipient or replay safety limits may apply. Reuse the same Vodafone idempotencyKey when checking an existing attempt.",
"EGRESS_FINGERPRINT_STALE": "The sending address changed. Run akedly byo-sms vodafone readiness, check the new address list, then acknowledge again with the new fingerprint.",
"VODAFONE_EGRESS_NOT_READY": "Run akedly byo-sms vodafone readiness; ask Akedly support to verify the outbound addresses before acknowledging.",
"UNROUTABLE_COUNTRY": "Run akedly byo-sms countries and choose routable country codes; use ALL alone for catch-all coverage.",
"SOURCE_COVERAGE_UNROUTABLE": "Update the source integration countries using akedly byo-sms countries, then clone again.",
"INVALID_TEST_PHONE": "Supply --test-phone-number in international format or update the saved test destination.",
"TEST_PHONE_OUT_OF_COVERAGE": "Choose a test number in the integration countries, or update its countries before testing.",
"VODAFONE_TEST_GUARD_UNAVAILABLE": "No new test was sent. Retry with the same idempotencyKey; contact Akedly support if send protection remains unavailable.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_REQUIRED": "Supply --idempotency-key with a unique request key and reuse it after an uncertain response.",
"VODAFONE_TEST_IDEMPOTENCY_KEY_INVALID": "Supply --idempotency-key with 1 to 255 characters.",
"VODAFONE_ACCEPTED_RETEST_INVALID": "Supply --previous-test-key with the accepted test key before requesting another test.",
"VODAFONE_ACCEPTED_RETEST_REQUIRED": "Check akedly byo-sms get <id>; to send again after an accepted result, supply --previous-test-key and a new --idempotency-key with --yes.",
"VODAFONE_NEW_TEST_CONFIRMATION_REQUIRED": "Check the previous result with akedly byo-sms get <id>; --allow-new-after-unknown with a new --idempotency-key and --yes can send another message.",
"VODAFONE_NEW_TEST_IN_PROGRESS": "Wait for the current Vodafone test result; inspect akedly byo-sms get <id> before starting another attempt.",
"BYO_SMS_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"VODAFONE_CONFIGURATION_CHANGED": "Run akedly byo-sms get <id>, test the current configuration, then try activation again.",
"BYO_SMS_STEPS_INVALID": "Check the steps JSON and secret names; secret and extraction names use letters, numbers and underscore."
},
"commandHints": {},
"hintPrecedence": [
"explicit handler hint",
"command-specific hint",
"global code hint",
"HTTP 429 fallback",
"generic command-help fallback"
],
"exitCodes": {
"USAGE": 64,
"CONFIRMATION_REQUIRED": 65,
"LOGIN_REQUIRED": 66,
"PERMISSION_DENIED": 67,
"ORG_ADMIN_REQUIRED": 67,
"OWNER_REQUIRED": 67,
"CLI_READ_ONLY": 67,
"IMPERSONATION_BLOCKED": 67,
"SELF_IDENTITY_REQUIRED": 67,
"CLI_LOGIN_OWNER_PROTECTED": 67,
"NEEDS_BROWSER": 68,
"SETUP_REQUIRED": 68,
"CLI_RATE_LIMITED": 69,
"NOT_AVAILABLE_ON_SERVER": 70,
"CLI_UPGRADE_REQUIRED": 70,
"CLI_DISABLED": 70
}
}
