xapi-to 0.1.20 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,210 @@
1
+ # Binance Web3 API Guide
2
+
3
+ The official `Binance Web3 API` service uses the action prefix
4
+ `binance-web3-api.` and currently exposes 58 Crypto actions for market data,
5
+ address analytics, RWA data, DEX aggregation, wallet balances, transaction
6
+ data/building/broadcast, and DeFi data and transaction building.
7
+
8
+ Do not confuse it with `binance-web3.` (Binance Web3 Intelligence) or
9
+ `binance-spot.` (Binance Spot). They are separate services with different
10
+ actions, schemas, and upstream behavior.
11
+
12
+ ## Discovery and shared limit
13
+
14
+ Discover the service ID instead of persisting a database UUID in automation:
15
+
16
+ ```bash
17
+ npx xapi-to services --category Crypto --page-size 100
18
+ npx xapi-to list --source api \
19
+ --service-id <binance-web3-api-service-id> --page-size 100
20
+ npx xapi-to get binance-web3-api.api_v1_dex_market_token_search
21
+ ```
22
+
23
+ The live catalog currently declares a service-level limit of **10 requests per
24
+ second per user**, shared across every API key and all 58 endpoints. Pace the
25
+ combined workload, not each action independently. The current fixed listed
26
+ price is `$0/call`; re-check `get` because limits and pricing can change.
27
+
28
+ GET actions take `{"method":"GET","params":{...}}` (plus `pathParams` when
29
+ the schema requires them). Chain identifiers are Binance IDs such as `"1"`
30
+ for Ethereum, `"56"` for BSC, and `"CT_501"` for Solana. They are not the
31
+ BlockPI network names or the built-in `crypto.*` chain enum.
32
+
33
+ ## Safe read examples
34
+
35
+ Discover the current chain list before relying on remembered IDs:
36
+
37
+ ```bash
38
+ npx xapi-to call binance-web3-api.api_v1_dex_market_supported_chain \
39
+ --input '{"method":"GET"}'
40
+ ```
41
+
42
+ Search by symbol/address, then use the returned contract and chain ID:
43
+
44
+ ```bash
45
+ npx xapi-to call binance-web3-api.api_v1_dex_market_token_search --input '{
46
+ "method":"GET","params":{"chains":"1,56,CT_501","search":"USDT"}
47
+ }'
48
+
49
+ npx xapi-to call binance-web3-api.api_v1_dex_market_candles --input '{
50
+ "method":"GET","params":{
51
+ "binanceChainId":"1",
52
+ "tokenContractAddress":"0xdac17f958d2ee523a2206206994597c13d831ec7",
53
+ "bar":"1h","limit":100
54
+ }
55
+ }'
56
+ ```
57
+
58
+ `candles.limit` is currently 1–300. `before` and `after` are exclusive Unix
59
+ millisecond bounds. Read each endpoint's enum independently; for example,
60
+ portfolio `timeFrame` values differ from leaderboard values.
61
+
62
+ RWA search supports ticker, company name, or contract address:
63
+
64
+ ```bash
65
+ npx xapi-to call binance-web3-api.api_v1_dex_market_rwa_search --input '{
66
+ "method":"GET","params":{"keyword":"NVDA","platformId":"ondo"}
67
+ }'
68
+ ```
69
+
70
+ `platformId` currently accepts `ondo` or `bstock` and is optional.
71
+
72
+ ## Quote and transaction boundary
73
+
74
+ Aggregator quote amounts are positive integer strings in the sell token's
75
+ smallest unit. A quote is not a swap and does not authorize signing:
76
+
77
+ ```bash
78
+ npx xapi-to call binance-web3-api.api_v1_dex_aggregator_quote --input '{
79
+ "method":"GET","params":{
80
+ "amount":"1000000","binanceChainId":"56",
81
+ "fromTokenAddress":"0x55d398326f99059fF775485246999027B3197955",
82
+ "toTokenAddress":"0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"
83
+ }
84
+ }'
85
+ ```
86
+
87
+ The normal flow is `quote` → choose a returned route/`quoteId` (about 30-second
88
+ TTL) → `swap` to build transaction data. `quote-and-swap` combines the first
89
+ two steps when a vendor is selected. EVM approvals and swaps, Solana compiled
90
+ transactions/instructions, RFQ typed data, and DeFi transaction endpoints
91
+ return data for the caller to inspect, sign, and submit. They do not authorize
92
+ xAPI or an agent to sign on the user's behalf.
93
+
94
+ Before any sign or broadcast step, obtain explicit approval for the selected
95
+ chain, wallet, token addresses, exact amounts, route/vendor, slippage, fees,
96
+ approval allowance, recipient, gas policy, and expected transaction effects.
97
+ Never pass a private key or seed phrase to xAPI.
98
+
99
+ ## Current POST schema gap
100
+
101
+ At the time this guide was verified, the serving XAPI action catalog omitted
102
+ the `body` from all 18 body-bearing POST operations even though the provider's
103
+ current import payload contained those schemas. The nineteenth POST operation,
104
+ `token_basic-info`, is intentionally query-only and exposes required `params`
105
+ (`binanceChainId` and `tokenContractAddress`). This mismatch is serving-contract
106
+ drift, not evidence that the official operations take no input.
107
+
108
+ Do not copy a Binance-native request body or invent a `body`. For an action that
109
+ needs request content, wait until `npx xapi-to get <action-id>` exposes it and
110
+ report the service-schema gap instead. The query-only token basic-info action
111
+ may be called exactly as its live `params` schema declares. Once fixed, the
112
+ live xAPI schema—not this snapshot—is authoritative.
113
+
114
+ ## Provider errors
115
+
116
+ Binance may return a successful HTTP response with a nonzero business code.
117
+ Treat `code: 0` as success and preserve the upstream code/message on failure.
118
+ In particular:
119
+
120
+ - `40304` means a regional compliance restriction; changing parameters or
121
+ retrying cannot bypass it.
122
+ - `40104` means the configured upstream API key lacks permission for that
123
+ product; DeFi access requires explicit enablement.
124
+
125
+ Do not automatically retry either error or misreport it as an xAPI-key failure.
126
+
127
+ ## Current action catalog (58)
128
+
129
+ ### General Data (13)
130
+
131
+ - `binance-web3-api.api_v1_dex_market_candles`
132
+ - `binance-web3-api.api_v1_dex_market_memepump_tokenDevInfo`
133
+ - `binance-web3-api.api_v1_dex_market_price`
134
+ - `binance-web3-api.api_v1_dex_market_price-info`
135
+ - `binance-web3-api.api_v1_dex_market_supported_chain`
136
+ - `binance-web3-api.api_v1_dex_market_token_advanced-info`
137
+ - `binance-web3-api.api_v1_dex_market_token_basic-info`
138
+ - `binance-web3-api.api_v1_dex_market_token_holder`
139
+ - `binance-web3-api.api_v1_dex_market_token_hot-token`
140
+ - `binance-web3-api.api_v1_dex_market_token_search`
141
+ - `binance-web3-api.api_v1_dex_market_token_top-liquidity`
142
+ - `binance-web3-api.api_v1_dex_market_token_top-trader`
143
+ - `binance-web3-api.api_v1_dex_market_trades`
144
+
145
+ ### Address Portfolio (7)
146
+
147
+ - `binance-web3-api.api_v1_dex_market_address-tracker_trades`
148
+ - `binance-web3-api.api_v1_dex_market_leaderboard_list`
149
+ - `binance-web3-api.api_v1_dex_market_portfolio_dex-history`
150
+ - `binance-web3-api.api_v1_dex_market_portfolio_overview`
151
+ - `binance-web3-api.api_v1_dex_market_portfolio_recent-pnl`
152
+ - `binance-web3-api.api_v1_dex_market_portfolio_supported_chain`
153
+ - `binance-web3-api.api_v1_dex_market_portfolio_token_latest-pnl`
154
+
155
+ ### RWA Data (6)
156
+
157
+ - `binance-web3-api.api_v1_dex_market_rwa_platforms`
158
+ - `binance-web3-api.api_v1_dex_market_rwa_price`
159
+ - `binance-web3-api.api_v1_dex_market_rwa_search`
160
+ - `binance-web3-api.api_v1_dex_market_rwa_tokens`
161
+ - `binance-web3-api.api_v1_dex_market_rwa_underlying-market`
162
+ - `binance-web3-api.api_v1_dex_market_rwa_underlying-profile`
163
+
164
+ ### Trading API (9)
165
+
166
+ - `binance-web3-api.api_v1_dex_aggregator_approve-transaction`
167
+ - `binance-web3-api.api_v1_dex_aggregator_history`
168
+ - `binance-web3-api.api_v1_dex_aggregator_order_{orderId}`
169
+ - `binance-web3-api.api_v1_dex_aggregator_quote`
170
+ - `binance-web3-api.api_v1_dex_aggregator_quote-and-swap`
171
+ - `binance-web3-api.api_v1_dex_aggregator_supported_chain`
172
+ - `binance-web3-api.api_v1_dex_aggregator_swap`
173
+ - `binance-web3-api.api_v1_dex_aggregator_swap-instruction`
174
+ - `binance-web3-api.api_v1_dex_aggregator_order_submit`
175
+
176
+ ### Transaction API (7)
177
+
178
+ - `binance-web3-api.api_v1_dex_post-transaction_orders`
179
+ - `binance-web3-api.api_v1_dex_pre-transaction_block-height`
180
+ - `binance-web3-api.api_v1_dex_pre-transaction_broadcast-transaction`
181
+ - `binance-web3-api.api_v1_dex_pre-transaction_gas-limit`
182
+ - `binance-web3-api.api_v1_dex_pre-transaction_gas-price`
183
+ - `binance-web3-api.api_v1_dex_pre-transaction_simulate`
184
+ - `binance-web3-api.api_v1_dex_pre-transaction_supported_chain`
185
+
186
+ ### Wallet API (5)
187
+
188
+ - `binance-web3-api.api_v1_dex_balance_all-token-balances-by-address`
189
+ - `binance-web3-api.api_v1_dex_balance_supported_chain`
190
+ - `binance-web3-api.api_v1_dex_balance_token-balances-by-address`
191
+ - `binance-web3-api.api_v1_dex_post-transaction_transaction-detail-by-txhash`
192
+ - `binance-web3-api.api_v1_dex_post-transaction_transactions-by-address`
193
+
194
+ ### DeFi Data and Transaction (11)
195
+
196
+ - `binance-web3-api.api_v1_defi_data_investment_detail`
197
+ - `binance-web3-api.api_v1_defi_data_investment_list`
198
+ - `binance-web3-api.api_v1_defi_data_position_list`
199
+ - `binance-web3-api.api_v1_defi_data_protocol_detail`
200
+ - `binance-web3-api.api_v1_defi_data_protocol_list`
201
+ - `binance-web3-api.api_v1_defi_transaction_claim`
202
+ - `binance-web3-api.api_v1_defi_transaction_deposit`
203
+ - `binance-web3-api.api_v1_defi_transaction_lp-add`
204
+ - `binance-web3-api.api_v1_defi_transaction_lp-add_calculate`
205
+ - `binance-web3-api.api_v1_defi_transaction_lp-remove`
206
+ - `binance-web3-api.api_v1_defi_transaction_redeem`
207
+
208
+ Use `list --service-id` to detect additions/removals and `get` immediately
209
+ before each call. Do not infer that similarly named endpoints share parameters,
210
+ enum values, pagination, or response shapes.
@@ -0,0 +1,112 @@
1
+ # BlockPI RPC Guide
2
+
3
+ The `rpc` third-party service exposes EVM JSON-RPC through BlockPI. The current
4
+ catalog has one generic action and 12 legacy convenience actions over 60
5
+ explicitly registered mainnets and testnets. The server supplies the BlockPI
6
+ partner credential; callers send only their xAPI key to xAPI and must never ask for, expose, or forward the upstream credential.
7
+
8
+ Always inspect the live schema and price before calling:
9
+
10
+ ```bash
11
+ npx xapi-to get rpc.network
12
+ npx xapi-to search "BlockPI RPC" --source api --page-size 100
13
+ ```
14
+
15
+ The catalog currently lists each action at `$0.000003/call`; treat `get` as the
16
+ authority because pricing and supported networks may change.
17
+
18
+ ## Generic JSON-RPC
19
+
20
+ Prefer `rpc.network` for arbitrary EVM methods. It accepts exactly one JSON-RPC
21
+ request object, not a batch. Keep the HTTP method and JSON-RPC method separate:
22
+
23
+ ```bash
24
+ npx xapi-to call rpc.network --input '{
25
+ "method":"POST",
26
+ "pathParams":{"network":"ethereum"},
27
+ "body":{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}
28
+ }'
29
+ ```
30
+
31
+ The required outer fields are `method`, `pathParams`, and `body`.
32
+ `pathParams.network` selects the registered BlockPI network. The body requires
33
+ `jsonrpc: "2.0"` and the JSON-RPC `method`; `id` and `params` are optional.
34
+
35
+ The generic route fails closed with HTTP 503 when the server-side partner key
36
+ is unavailable; it never silently sends a raw RPC call without that credential.
37
+ The legacy convenience routes have a different availability contract and may
38
+ fall back to their configured public RPC endpoints. Diagnose the two route
39
+ families separately instead of treating every BlockPI 503 as a bad request.
40
+
41
+ For example, read an address balance without exposing any provider key:
42
+
43
+ ```bash
44
+ npx xapi-to call rpc.network --input '{
45
+ "method":"POST",
46
+ "pathParams":{"network":"base"},
47
+ "body":{"jsonrpc":"2.0","id":"balance-1","method":"eth_getBalance",
48
+ "params":["0x0000000000000000000000000000000000000000","latest"]}
49
+ }'
50
+ ```
51
+
52
+ ## Registered networks
53
+
54
+ The current `rpc.network` enum contains 60 values:
55
+
56
+ ```text
57
+ abstract, arbitrum, arbitrum-nova, arbitrum-sepolia, arc-testnet,
58
+ avalanche, avalanche-fuji, base, base-sepolia, berachain, blast, bsc,
59
+ bsc-testnet, celo, celo-sepolia, conflux-espace, cronos, ethereum,
60
+ ethereum-hoodi, ethereum-sepolia, etherlink, fantom, gnosis, hemi,
61
+ hyperliquid, ink, kaia, kaia-kairos, linea, linea-sepolia, mantle, merlin,
62
+ merlin-testnet, meter, metis, monad, monad-testnet, optimism,
63
+ optimism-sepolia, plasma, plume, polygon, polygon-amoy, robinhood, scroll,
64
+ scroll-sepolia, sei-evm, sei-testnet-evm, sonic, stable, story, taiko,
65
+ unichain, unichain-sepolia, viction, xlayer, zetachain-athens-evm,
66
+ zetachain-evm, zksync-era, zksync-era-sepolia
67
+ ```
68
+
69
+ Do not normalize or invent aliases. Re-run `get rpc.network` and use its exact
70
+ enum when a network is absent or when the task depends on current support.
71
+
72
+ ## Legacy convenience actions
73
+
74
+ These actions translate simple REST-shaped inputs to common EVM methods:
75
+
76
+ - `rpc.network` — arbitrary single JSON-RPC request
77
+ - `rpc.chain_blockNumber` — latest block number
78
+ - `rpc.chain_call` — `eth_call`
79
+ - `rpc.chain_chainId` — chain ID
80
+ - `rpc.chain_gasPrice` — gas price
81
+ - `rpc.chain_getBalance` — native balance
82
+ - `rpc.chain_getBlockByHash` — block by hash
83
+ - `rpc.chain_getBlockByNumber` — block by number/tag
84
+ - `rpc.chain_getCode` — contract bytecode
85
+ - `rpc.chain_getLogs` — event logs
86
+ - `rpc.chain_getTransactionByHash` — transaction by hash
87
+ - `rpc.chain_getTransactionCount` — address nonce
88
+ - `rpc.chain_getTransactionReceipt` — transaction receipt
89
+
90
+ Legacy actions use `pathParams.chain` plus action-specific query `params`.
91
+ Never infer those parameters from the raw JSON-RPC signature; inspect the exact
92
+ action first:
93
+
94
+ ```bash
95
+ npx xapi-to get rpc.chain_getBalance
96
+ npx xapi-to call rpc.chain_getBalance --input '{
97
+ "method":"POST",
98
+ "pathParams":{"chain":"ethereum"},
99
+ "params":{"address":"0x0000000000000000000000000000000000000000",
100
+ "block":"latest"}
101
+ }'
102
+ ```
103
+
104
+ ## Transaction safety
105
+
106
+ Reads such as block, balance, code, logs, and receipt queries are non-mutating.
107
+ Methods such as `eth_sendRawTransaction` can create irreversible on-chain
108
+ effects even though they use the same generic action. Before submitting a
109
+ signed transaction, obtain explicit approval for the chain, sender, recipient,
110
+ value, calldata, gas policy, and transaction hash workflow. Do not send private
111
+ keys or seed phrases through xAPI, and do not automatically retry an ambiguous
112
+ broadcast. Check the transaction hash or sender nonce first.
@@ -0,0 +1,189 @@
1
+ # Domains and DNS Guide
2
+
3
+ Use the built-in `domain.*` and `dns.*` capabilities to search, price, register,
4
+ list, and inspect domains, then manage their DNS records. These are capability
5
+ actions, not third-party API actions, so discover them with
6
+ `--source capability` and pass a flat JSON object to `--input`.
7
+
8
+ Domain registration is a real, non-refundable purchase. DNS changes alter live
9
+ traffic. Read the current schema, show the user the exact target and price or
10
+ record change, and obtain explicit approval before either mutation.
11
+
12
+ ## Current actions
13
+
14
+ | Action | Purpose | Mutation |
15
+ |---|---|---|
16
+ | `domain.search` | Registrar suggestions and one-year estimated prices | No |
17
+ | `domain.check` | Check exact-domain availability | No |
18
+ | `domain.price` | Read the current USD registration price | No |
19
+ | `domain.register` | Register a domain | **Purchase** |
20
+ | `domain.registration.get` | Read an asynchronous registration task | No |
21
+ | `domain.list` | List domains owned through xAPI | No |
22
+ | `domain.get` | Inspect one domain by `domain_id` | No |
23
+ | `dns.list` | List a domain's DNS records | No |
24
+ | `dns.upsert` | Create or update a DNS record | **Write** |
25
+ | `dns.delete` | Delete a DNS record | **Write** |
26
+ | `dns.dnssec.get` | Read desired, effective, and provider DNSSEC state | No |
27
+ | `dns.dnssec.set` | Enable or disable Cloudflare DNSSEC | **Write** |
28
+
29
+ Fetch the live schemas before use:
30
+
31
+ ```bash
32
+ npx xapi-to get-batch domain.search domain.check domain.price domain.register \
33
+ domain.registration.get domain.list domain.get dns.list dns.upsert dns.delete \
34
+ dns.dnssec.get dns.dnssec.set
35
+ ```
36
+
37
+ ## Search, check, and price
38
+
39
+ `domain.search` accepts a keyword and up to 20 optional TLDs. Its availability
40
+ and one-year prices are suggestions, not a purchase quote. Search first, then
41
+ check and price the exact fully qualified domain:
42
+
43
+ ```bash
44
+ npx xapi-to call domain.search \
45
+ --input '{"keyword":"example","tlds":["com","dev","ai"]}'
46
+
47
+ npx xapi-to call domain.check --input '{"domain":"example.com"}'
48
+ npx xapi-to call domain.price --input '{"domain":"example.com","period":1}'
49
+ ```
50
+
51
+ `domain.price` is the authoritative pre-registration price at call time. The
52
+ current USD billable price includes xAPI's fixed fee; non-USD registrar quotes
53
+ are unsupported. Re-price immediately before registration because availability
54
+ and upstream prices can change.
55
+
56
+ ## Register a domain
57
+
58
+ Before calling `domain.register`:
59
+
60
+ 1. Re-run `domain.check` and `domain.price` for the exact domain and period.
61
+ 2. Show the exact domain, period, current USD price, and `max_price_usd` ceiling.
62
+ 3. Confirm the registrant contact details and obtain explicit purchase approval.
63
+ 4. Create one idempotency key for that exact request and reuse it only for retries.
64
+
65
+ The required contact fields are `first_name`, `last_name`, `address1`, `city`,
66
+ `state`, `postal_code`, `country`, `phone`, and `email`. `country` is a two-letter
67
+ ISO code. Phone numbers must use `+{country_code}.{number}`, for example
68
+ `+86.13800138000`. Do not log contact data or include it in task summaries.
69
+
70
+ ```bash
71
+ npx xapi-to call domain.register --input '{
72
+ "domain":"example.com",
73
+ "period":1,
74
+ "max_price_usd":20,
75
+ "idempotency_key":"register-example-com-20260910",
76
+ "auto_renew":false,
77
+ "whois_privacy":true,
78
+ "contact":{
79
+ "first_name":"Given","last_name":"Family",
80
+ "address1":"Street address","city":"City","state":"Region",
81
+ "postal_code":"000000","country":"CN",
82
+ "phone":"+86.13800138000","email":"owner@example.com"
83
+ }
84
+ }'
85
+ ```
86
+
87
+ `max_price_usd` is a hard final-charge ceiling, not the expected price.
88
+ `auto_renew` must currently remain `false`; renewal billing is not available.
89
+ Registration is non-refundable. A successful submission can return a `task_id`
90
+ before the registrar has finished. Poll that exact task rather than repeating the
91
+ purchase:
92
+
93
+ ```bash
94
+ npx xapi-to call domain.registration.get \
95
+ --input '{"task_id":"<task-id-from-domain-register>"}'
96
+ ```
97
+
98
+ Continue polling only while `status` is `pending` or `processing`. Treat
99
+ `succeeded`, `failed`, and `expired` as terminal. Preserve `phase`, `action`,
100
+ `error`, `final_cost`, and `confirmation_sent_to` when reporting the result;
101
+ some registrations can require external confirmation or manual review.
102
+
103
+ Do not retry with a new idempotency key after an ambiguous response. If a
104
+ `task_id` was returned, read it with `domain.registration.get`. Otherwise reuse
105
+ the original key only for the exact same request, then inspect `domain.list`
106
+ before deciding whether another purchase attempt is safe.
107
+
108
+ `domain.get` intentionally does not return the registrant contact. Treat that
109
+ privacy boundary as expected rather than assuming registration lost the data.
110
+
111
+ ## DNS workflow
112
+
113
+ DNS actions use the xAPI `domain_id`, not the domain name. Resolve it with
114
+ `domain.list`, inspect the current records, and retain the stable `record_id`:
115
+
116
+ ```bash
117
+ npx xapi-to call domain.list --input '{"limit":50,"offset":0}'
118
+ npx xapi-to call dns.list --input '{"domain_id":"<domain-id>"}'
119
+ ```
120
+
121
+ Create a record by omitting both record identifiers:
122
+
123
+ ```bash
124
+ npx xapi-to call dns.upsert --input '{
125
+ "domain_id":"<domain-id>",
126
+ "subdomain":"@","type":"A","value":"203.0.113.10",
127
+ "idempotency_key":"dns-create-root-a-20260910"
128
+ }'
129
+ ```
130
+
131
+ Update an existing record with its stable `record_id`. `record_index` is a
132
+ legacy fallback whose meaning can change when the record set changes; use it
133
+ only when a live response lacks `record_id`.
134
+
135
+ ```bash
136
+ npx xapi-to call dns.upsert --input '{
137
+ "domain_id":"<domain-id>","record_id":"<record-id>",
138
+ "subdomain":"www","type":"CNAME","value":"example.com.",
139
+ "idempotency_key":"dns-update-www-20260910"
140
+ }'
141
+
142
+ npx xapi-to call dns.delete --input '{
143
+ "domain_id":"<domain-id>","record_id":"<record-id>",
144
+ "idempotency_key":"dns-delete-record-20260910"
145
+ }'
146
+ ```
147
+
148
+ For every write, confirm the domain, record type/name/value, and stable record
149
+ identifier. Reuse an idempotency key only for an identical retry; use a new key
150
+ when any requested value changes. After a successful write, call `dns.list`
151
+ again and verify the intended state instead of assuming propagation or success.
152
+
153
+ ## DNSSEC lifecycle
154
+
155
+ DNSSEC is currently available only for domains whose authoritative provider is
156
+ Cloudflare. Read the current state before changing it:
157
+
158
+ ```bash
159
+ npx xapi-to call dns.dnssec.get \
160
+ --input '{"domain_id":"<domain-id>"}'
161
+ ```
162
+
163
+ After explicit approval, request the desired state with one stable idempotency
164
+ key. Reuse that key only when retrying this exact domain and `enabled` value:
165
+
166
+ ```bash
167
+ npx xapi-to call dns.dnssec.set --input '{
168
+ "domain_id":"<domain-id>",
169
+ "enabled":true,
170
+ "idempotency_key":"dnssec-enable-example-20260917"
171
+ }'
172
+ ```
173
+
174
+ The mutation can finish its request while Cloudflare or the parent registry is
175
+ still reconciling. Interpret the response fields together:
176
+
177
+ - `desired_enabled` is the requested target.
178
+ - `effective_enabled` is the state currently protecting DNS responses.
179
+ - `transition` is `enabling`, `disabling`, or `null` when settled.
180
+ - `provider_status` preserves the upstream lifecycle state.
181
+ - `action_required` means operator intervention is needed.
182
+
183
+ `pending` and `pending-disabled` are transitions, not success. Poll
184
+ `dns.dnssec.get` until the state settles, becomes `error`, or the user's
185
+ deadline is reached. Do not report DNSSEC as enabled until
186
+ `effective_enabled=true` with no transition, and do not report it as disabled
187
+ until `effective_enabled=false` with no transition. Preserve DS metadata when
188
+ the caller needs to inspect delegation, but never invent or manually publish a
189
+ DS record unless the live response explicitly says operator action is required.