Pay for and call a service
pay_servicePay for a catalog service from your spend-capped delegation and return the vendor response. This charges real funds. Paid calls can take several seconds, including payment verification, service work and settlement; longer tasks can take more. Call get_service FIRST: its requestShape.endpoints[] lists the callable paths with their HTTP method and price, and each payServiceArgs is the slug/path/method to pass here, after replacing any pathParams placeholder in path with a real value. A bare slug reaches the service base URL, which for a multi-endpoint API answers 404 instead of a payment challenge; only a single-endpoint service is called by slug alone. Send the endpoint's payServiceArgs: they carry an example only when it is paid-run or challenge. A docs example (and its invokePath) is unverified — use it only deliberately, filling any path parameter with the entity you want; otherwise build body from the endpoint description or the provider's docs. method may be omitted: it is taken from the catalog endpoint matching path (POST when the catalog names none) and echoed back under request. To accept a live quote, pass its quoteId before expiresAt with the exact same call arguments; the Router uses that cached challenge and exact fee-inclusive total. Keep maxTotalCents as an independent ceiling. Repeating an identical call (same arguments, no fresh) charges nothing new: on a deployment running API 1.48 or later it is answered within 24 hours from the first call's stored result, or its pending answer; on an older deployment it comes back as already_paid. For an ASYNC service, where you submit once and then poll a status endpoint with the same id, set fresh: true on every poll INCLUDING THE FIRST: otherwise each later poll reuses the first poll's idempotency key and gets the first poll's stored answer (or already_paid) back, so the status never advances. fresh makes each poll a distinct, separately billed call (the merchant charges per status call). Leave fresh off the submit and off any retry of a call whose outcome is unsure, because with fresh a retry is not de-duplicated and is charged again. A completed call returns paid, upstreamStatus (the vendor's HTTP status) and response (its body). paid: true means a payment was attempted, not that it settled, so read payment.status (Settled, Issued or Failed): Issued is not confirmed yet, which is neither a failure nor a reason to retry, and Failed does not prove nothing was charged, so check deliveryStatus in list_payments before paying again. With a non-2xx upstreamStatus it may still have cost the merchant price. Outcomes to act on: {"status":"pending"} means a payment was attempted and the service is still working, so whether it went through is not known yet; call get_payment_result with its paymentId every few seconds instead of calling pay_service again. {"status":"already_paid"} means this call's idempotency key already carries a payment whose result is not replayed here (the original call failed, its 24-hour result expired, the requestId was reused for a different request, or the deployment predates result replay, API 1.48); nothing new was charged, and it does not by itself say whether the original succeeded. Read the original with get_payment_result (paymentId) or list_payments (pass its requestId). A new requestId starts a separate, separately charged purchase: use one only after that read shows the original failed and the human still wants the result. {"error":"payment_indeterminate"} means the charge may or may not have landed: check list_payments first, and to retry reuse the returned requestId verbatim without fresh so the retry stays idempotent. {"error":"per_call_max_exceeded"} means no charge; raise maxTotalCents only after a deliberate decision and retry with the same requestId. quote_not_found, quote_expired, and quote_mismatch are named no-charge quote refusals; quote again rather than paying unpinned. {"error":"payment_failed"} is a definite decline (see code, message and retryable). These charge nothing: {"payable":false} (not payable via the Router; no upstream URL is ever returned), service_not_found (wrong slug), catalog_unavailable and delegation_lookup_failed (transient; retry later), payment_not_started (too little time remained to start a payment; retry with the same arguments), no_delegation (call setup_delegation), router_controls_unavailable (the API behind this server is too old to enforce search, quoteId or maxTotalCents; tell the human instead of retrying without them) and credential_refused (re-authorize). A paid result also carries delegationId and budget, the spending budget after this call (capCents/spentCents/remainingCents, cents with up to four decimals), so to say what has been spent or what is left, repeat budget rather than adding up your calls yourself; budget: null means it could not be read this time, not that it is zero (call get_budget). FUNDING (funding): omit it and the call is paid with your Nevermined credits when you hold credits that can pay it (a Nevermined Subscription allowance, welcome or top-up credits) and the endpoint carries a credits tariff (Nevermined pays the merchant; the call costs the tariff in credits, returned if the service does not answer successfully), otherwise from your delegation; pass "subscription" or "delegation" to choose. The price ceiling, and the credits left of a subscriber in good standing, are checked only when the call is made: a credits call refused for them (insufficient_credits, subscription_cap_reached, subscription_ceiling_exceeded) charges nothing and is NOT switched to your delegation, so follow its alternative. Without a subscription in good standing, your spendable credits are checked before the call, and a call they cannot cover (when no automatic credit pack would) is paid from your delegation, with fundingCode saying why. Every result that mentions funding states which funding was chosen: a subscription call returns funding: "subscription" with creditsCharged, creditsStatus and creditsLeft (a digit string read after the call; null means unavailable, not zero) (Committed = spent, Reserved = held or marked for refund, Refunded = returned) and no budget, plus topUp (credits, amountUsd) and topUpMessage when the call ran out of credits and charged one credit pack to the saved card to cover itself (credit top-ups on, and only on a call made with your own API key: a call made through an OAuth connection never charges a pack) — relay topUpMessage to the user, since their card was charged (it comes alone, without topUp, when the pack could not be read); quoteId, maxTotalCents and delegationId apply only to a delegation. already_paid states no funding: it answers for an earlier payment, which may have been funded either way, and a retry that lands on the other funding gets it even when the original succeeded and its result is still kept. To see which funding that payment used, read fundingSource from list_payments with its requestId. Subscription refusals charge nothing and no credits (unless they carry topUps: "bought", below): subscription_unavailable, no_active_subscription, subscription_not_eligible (see reason), subscription_rail_unsupported, subscription_ceiling_exceeded, insufficient_credits, credits_frozen (every credit frozen by a card dispute; on a call with funding omitted and no delegation it names setup_delegation), subscription_cap_reached, credit_daily_limit_exceeded (wait until midnight UTC or ask the human to change their limit), credit_budget_unavailable (retry later) and subscription_payment_refused. Neither daily-budget refusal switches to a delegation; credit_daily_limit_exceeded carries dailyLimitCredits (number), usedCredits, remainingCredits (digit strings) and resetsAt. Other refusals say in alternative when the delegation can pay instead. With credit top-ups on, insufficient_credits on a call made with your own API key carries topUps, and a credit pack may have been charged even though the call was refused (no credits were spent on it); on a call made through an OAuth connection it carries none and no pack was charged: paused (a pack was declined; buying one from the Nevermined dashboard resumes them), limitReached (the monthly top-up limit), pending (a pack is being charged and may already be; retry in a moment), unavailable (no pack was charged; retry later) or bought (this call charged a pack but other calls spent it first; on code BCK.SUBSCRIPTION_ROUTER.0009 retry, otherwise follow message). credits_frozen can carry topUps: "bought" too: a pack was charged, then frozen with the rest by a dispute. Any refusal with topUps: "bought" also carries topUpMessage and the pack’s purchaseId: relay topUpMessage to the user, since their card was charged. Requires your Nevermined API key on the Authorization header.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Request payload sent to the vendor. | |
| path | No | Router suffix appended to the service base. Read `get_service`'s `requestShape.endpoints[].payServiceArgs.path`: a checked `invokePath` may be empty even when the display path is not. Replace every `pathParams` placeholder. Put query parameters in `search`, never in `path`. | |
| slug | Yes | The service slug to pay for. | |
| fresh | No | Set true on every POLL of an async status endpoint, including the first: each call mints a fresh idempotency key so the poll advances, instead of later polls reusing the derived key and getting the first poll's stored answer (or, before API 1.48, `already_paid`) back. Trade-off: with `fresh` a retry is NOT de-duplicated and WILL be charged again — use it to advance a poll, never to retry a call whose outcome you are unsure of. Leave unset for ordinary calls so a genuine retry stays idempotent. Ignored when you pass an explicit requestId. | |
| method | No | HTTP method for the vendor call. Omit to use the method the catalog records for the endpoint matching `path`; falls back to POST when the catalog records none. | |
| search | No | Query string without ?, for a slug-routed GET (e.g. flight_iata=AA217). | |
| funding | No | How to pay: `subscription` (your Nevermined credits) or `delegation` (your spend-capped delegation). Omit it to use your credits when the endpoint carries a credits tariff and they are accepted for it (without a subscription in good standing, only when they cover the call), else the delegation; a credits call refused when it is made, because too few credits are left or by the price ceiling, is NOT switched to the delegation. The result says which was chosen. | |
| headers | No | Extra headers for the vendor call. | |
| quoteId | No | Opaque short-lived quote id returned by quote_service. Pass it only with the exact same call before expiresAt; the Router then pays the cached challenge and exact fee-inclusive total. Keep maxTotalCents as an independent ceiling. | |
| requestId | No | Idempotency key. Leave unset — a retry of the same call is de-duplicated automatically. Only set a NEW value if you intend a genuinely separate, additional charge. To poll an async status endpoint, prefer `fresh: true` over minting your own value. | |
| delegationId | No | Delegation to charge when you authenticate with an API key; defaults to your active one. An OAuth-connected caller always spends from the grant it approved, so a value here does not choose the delegation; it still feeds the derived idempotency key when you omit `requestId`, so keep it the same across retries of one call. The result's `delegationId` names the delegation actually charged. | |
| maxTotalCents | No | Maximum whole cents this call may cost, including the buyer fee (compared exactly, so rounding alone never exceeds it). The price the service quotes on its 402 can differ from the catalog price label. | |
| requireProtocol | No | Force the payment rail. Set it only when you must pay on one rail — for example a wallet that holds funds on a single chain. If the service offers both rails the Router uses this one; if the service does not offer it the call is refused (`payment_failed` with code BCK.ROUTER.0045, nothing charged) rather than paid on the other rail. Omit it to let the Router auto-select (it prefers mpp). A `quoteId` that was quoted on the other rail is refused (`quote_mismatch`); quote again with the same `requireProtocol`. | |
| validateRequestBody | No | Opt-in safety check (default off). When true, and the request body provably fails the endpoint's published request schema, the Router refuses the call BEFORE anything is signed, minted or charged (`payment_failed` with code BCK.ROUTER.0046, nothing charged) — instead of paying and letting the merchant reject a malformed body. Leave it unset to let a questionable body through to the merchant unchecked. Only a provably-invalid body is refused; a missing or unverifiable schema still pays. Fix the body to match the endpoint's published requestSchema (see `get_service`), or drop this flag. |