Skip to content
Uno Docs

Recover an uncertain payment

This page covers what to do when a payment call to Uno did not return a clear result: a timeout, a lost reply, a pending payment or an ended quote. It is for developers and AI agents that buy Agent storage with the uno CLI or over HTTP. Follow the rules and the decision table, and you never pay a second time. The page is self-contained.

Rules

  1. Check before you pay again. Reading never pays. Run uno kit quota and uno kit purchase <purchase_id> first.
  2. Reuse the same --operation in the CLI. Over HTTP, reuse the same Idempotency-Key and the exact same body. Never use a new key or a new operation to recover.
  3. Reuse the same payment credential. Never send a second payment for the same purchase.
  4. Never pay while a payment may be in flight.
  5. Pay only the quoted network and token, and only after your user approves the exact amount.

Decision table

What you seeWhat to do
Lost response to the quoteReplay POST /v1/purchases with the same key and body. You get the same purchase and quote, or the recorded result. In the CLI, rerun the identical uno kit pay command with the same --operation. Never use a new key to recover.
Quote ended before paymentReplay the same key and body. After the quote end you get a new quote under the same purchase. Earlier quotes stay readable. The replay never pays. In the CLI, rerun the same --operation.
PendingPoll GET /v1/purchases/{link}/pay. In the CLI, run uno kit purchase <purchase_id>. Never pay again while a payment may be in flight. A pending attempt blocks only its own purchase. It never blocks another purchase.
SettledRead results. A replay returns the same result without a second charge or time grant.
Different capacity or months under a used keyThe request is refused as invalid_argument. Use a new key for a new purchase.
Expected-account mismatchStop. The call was pinned to a different account than the link or session names.
Lost reply after you paidDo not pay. Run uno kit quota and uno kit purchase <purchase_id>. If the purchase is not settled, rerun the same operation or poll the pay link.

The last row restates the rule: check before you pay again after any lost response, crash or new runtime.

Exit codes

The CLI uses these exit codes during a purchase. See also Agent mode and exit codes.

ExitNameMeaning in a purchaseWhat to do
14WRITE_PENDINGA payment is in flight, or a write is still pending and may land.Wait. Run uno kit purchase <purchase_id>. Never run a wallet command. For a write, rerun the identical command with the same --operation.
15PAYMENT_REQUIREDAn unpaid quote. error.details.payment holds the terms.Check uno kit quota and uno kit purchase <purchase_id> for an earlier payment. If none exists and your user approved the terms, run the printed pay_command.
21SEND_REJECTEDOffers are off, or the quote does not match your request, account or owner. The text says "not offered" or "Do not pay".Stop. Do not pay. Run uno kit quota later.
24NOT_AVAILABLEThe backend has no capacity purchases yet.Stop. Do not retry.
9ABORTEDThe purchase can no longer be paid, or its quote ended and was not refreshed. A write can also exit 9 on a stale head.Read the purchase with uno kit purchase <purchase_id>. See "Stale or expired quotes".

Retry a lost reply

Rerun the identical command with the same --operation:

uno kit pay --capacity 1GB --months 1 --operation buy-1 --format json

With --operation, the CLI reuses it as the server idempotency key. With a saved reply, it reads the owner purchase first. Otherwise it replays the quote request on the same key. It then does one of three things:

  • It prints the settled result.
  • It stops as pending with exit 14.
  • It prints the wallet command again for an unexpired quote.

The rerun never pays. Pay only with the wallet command, after you check that no earlier payment exists.

Without --operation, the command looks for an unexpired quote or a pending payment for the same capacity and months. It reuses the quote, or stops and points at uno kit purchase <purchase_id>. A bare rerun after payment would quote a new purchase, so always pass --operation.

A pending purchase never blocks a different operation.

Confirm the result

Read the purchase. This sends no payment:

uno kit purchase <purchase_id> --format json

data.owner.purchase.status is PURCHASE_STATUS_SETTLED when the payment is applied. results holds the outcome. Never report storage as bought before that status.

uno kit quota lists open and paid purchases from the account key alone. A fresh runtime imports the key, signs in with it, and finds them. Purchases come from the server, not from local files.

Over HTTP, an owner reads GET /v1/purchases/{id} with its Bearer credential. A payer with only the link polls GET /v1/purchases/{link}/pay with no credential.

Poll without paying

GET /v1/purchases/{link}/pay is receipt-only. It reconciles a pending payment against the network without broadcasting anything. It returns data.purchase.

Honor next_action.delay_seconds and the Retry-After header between polls. Only a POST to the same path, or a Payment retry on POST /v1/purchases, can pay. Never use them to check status.

Retry an HTTP payment

A payer that sends the Payment retry itself repeats the same request:

POST /v1/purchases HTTP/1.1
Host: mpp.uno.fun
Authorization: Payment PUBLIC_TEST_PAYMENT_NOT_A_CREDENTIAL
Content-Type: application/json
Idempotency-Key: public-test-buy-1
 
{"capacity_bytes":"1000000000","months_to_add":"1"}

Keep the same Idempotency-Key, the same body and the same credential. The retry carries no Bearer. The purchase and quote come from the verified challenge, not from your body. A success is HTTP 200 with a Payment-Receipt header.

If the purchase is already paid, the same call returns HTTP 200 with the immutable result. If a payment is in flight, it returns pending guidance with a poll next_action. It never returns a second challenge.

A retry of the write that needed storage is a separate request. Retry it with its original Idempotency-Key and exact body, after the purchase settles. A write never takes payment.

Resume a saved operation

Run uno kit resume <operation> after a lost response. It replays the saved request with the saved idempotency key. On an unpaid purchase it saves the challenge and prints the same payment text as uno kit pay, with exit 15.

An uncertain operation stops after 24 hours. The exception is a Pay journal that holds a saved signed authorization. For that payment, uno kit resume <operation> reuses the exact credential, request and idempotency key after the cutoff. Keep that journal until the payment settles or is definitely refused.

The Agent capacity command hands the wallet command to an external wallet. It journals the quote request, not payment bytes. For it, a local journal that is 24 hours or older is refused. Read the purchase with uno kit purchase <purchase_id>, then start a new operation if the purchase is not settled.

Keep the operation, the account pins and the original inputs. Resume refuses another account with exit 25.

Stale or expired quotes

A quote ends at the earlier of 30 minutes after creation and the plan's next expiry or restore cutoff. After that time it cannot be paid.

  • The quote ended, and the purchase is still open. Rerun the same operation. You get a new quote under the same purchase. The old quote stays readable. The rerun never pays. Pay the new quote only after you check its terms and your user approves them.
  • The purchase expired, failed, or comes from an old network. The CLI exits 9. These purchases are lookup-only. The CLI text says to keep the same operation and check the purchase before it requests another payment. Read the purchase with uno kit purchase <purchase_id>. If it is still unpayable, the CLI docs say to start a new operation to buy again.
  • A write exits 9 on a stale head. Read the ref and decide. Recovery never adopts a newer head.

Never silently pay a replacement quote.

Late, stale and duplicate payments

There is no cash refund. A late, stale or duplicate payment becomes storage time. The settled results name the outcome:

FieldMeaning
classificationquoted, stale, late or extra
applied_amountThe amount applied
capacity_bytesCapacity
starts_at, expires_atThe dates of the applied term
payment_txThe transaction hash

Settlement time is when the writer applied the payment, not the chain time.

When a purchase is already paid and another transfer arrives, the extra payment adds storage time. The response says "This purchase was already paid. The extra payment added N seconds of storage."

One transaction credits one purchase once. A replay returns the first result and never credits again. If Uno cannot credit a Tempo transfer again, it keeps the transfer for review and does not credit it twice. The response says "This extra payment is kept for review. Contact support with the transaction hash." Keep the transaction hash.