Skip to content

NUT-CTF Range Settlement

bitCaster uses NUT-CTF range settlement. It does not use a bilateral HTLC, peer ECDH, or adaptor-signature protocol.

The first-release server accepts only public FOK orders. The GUI and CLI submit FOK orders. Each public attempt uses one one-shot capability. FOK uses the book state at admission. It commits the full requested quantity or cancels the complete request. Public FAK, GTC, GTD, continuation, and residual reauthorization are not available.

bitCaster accepts monetary Cashu tokens with unit msat only. API payment and receipt amounts use msats. The GUI shows sats: 1,000 msats equals 1 sat. baseAsset: "sat" names the asset. It is not the token unit.

A Participation Score purchase must be a positive multiple of 1,000 msats. Each 1,000 msats buys one Score point. Receive fees do not reduce the purchased Score. Score points and receipt amounts are separate values.

A wallet authorizes one public FOK attempt with one PAY_TO_UNLOCK capability. Order admission checks that capability. Admission makes no mint network call.

The authorization covers its permitted range for that attempt. Public FOK does not rest on the book or leave a residual order. If the complete quantity cannot fill, the engine cancels the complete request. This cancellation does not spend the capability or trigger a refund.

Application errors from the capability API use RFC 9457 application/problem+json. Clients use the HTTP status and code. Do not classify errors from the wording of title or detail. Every code below starts with settlement-capability-. The type is /errors/ followed by the complete code.

HTTPCode suffixMeaning
400invalid-requestInvalid request fields.
400invalid-artifactInvalid or unsupported capability artifact.
400policy-rejectedCapability does not meet admission policy.
402score-requiredInsufficient Participation Score.
404not-foundCapability or result is unavailable to this caller.
409conflictCapability identity or state conflicts.
409market-unavailableMarket does not accept this capability.
413request-too-largeRequest exceeds a size or count limit.
429admission-limitedOutstanding admission work reached a limit.
429capacity-exhaustedAdmission processing capacity is exhausted.
503admission-unavailableSettlement admission is temporarily unavailable.

Capacity responses also retain limitCode and the diagnostic traceId. Missing results and results owned by another user have the same 404 response. Authentication, framework input validation, request-size limits, and rate limits can return a different body shape.

These are API request errors, not settlement-group states. An error alone does not authorize a refund or deletion of local records. Keep the original operation and capability when the response is uncertain. Retry the original request without changes.

Single-order submission also returns application Problem Details. The type is /errors/ followed by the code. Use the code, not the message wording.

HTTPCodeMeaning
400order-invalid-requestOrder request fields are invalid.
400order-invalid-commentThe attached comment is invalid.
403market-closedThe market is closed before submission.
404order-market-not-foundThe market is not registered.
404order-capability-not-foundThe capability is unavailable to this caller.
409order-capability-route-mismatchThe route differs from the bound order.
409order-capability-not-currentThe capability is not current for this order.
409order-book-conflictRetry the original request without changes.
409order-processing-conflictSubmission conflicts with the current order state.
409order-market-closedThe market closed during submission.
503order-admission-unavailableOrder admission is temporarily unavailable.
503order-processing-unavailableOrder processing is temporarily unavailable.

Only order-book-conflict is the retryable application 409. Do not apply that rule to every conflict. The SDK retains the original operation for 403 and 503 responses. An error does not prove that a refund is complete or authorize deletion of wallet records. An exact accepted replay returns the prior admission result. Missing and foreign capabilities have the same response. The shared closed-market check also returns market-closed for deposits. Framework errors can use another body shape.

These order endpoints use the same application Problem Details format.

HTTPCodeMeaning
400order-invalid-requestThe route, condition, or batch request is invalid.
403market-closedThe market is closed before batch submission.
404order-market-not-foundThe market for a batch request is not registered.
404order-not-foundThe order is unavailable for this request.
409order-batch-conflictThe order book changed during batch submission.
409order-market-closedThe market closed during batch submission.
409order-cancellation-conflictOrder state changed during cancellation.
429order-batch-limitedThe application batch rate limit was reached.

A whole-request batch error does not prove that no item was accepted. Reconcile the original item identities before retrying without changes. Successful batch responses retain their separate per-item results. Do not use the single-order submission error classifier for a batch.

Cancellation returns the same 404 for missing, foreign, and wrong-route orders. An already-terminal cancellation succeeds. Batch cancellation remains available after market closure. A notification lookup failure after a committed batch cancellation does not change its successful result.

Order-status reads return 404 for an absent or wrong-route order, and 403 for a found order owned by another user. The latter is an authorization response, not a market-closed error. The SDK returns null for a status-read 404 and false for a cancellation 404. With no registered market, order-list and order-book reads still return successful empty data. This is distinct from a failed read. Framework errors can use another body shape.

POST /api/v1/cashu-deliveries/{deliveryId} submits an exact delivery. GET on the same path reads its saved status. A failed request does not prove that no funds were received. Keep the original delivery ID, token, and immutable request for recovery. Do not create a replacement payment for an uncertain result.

The following application errors use application/problem+json. Their type is /errors/{code}. Use the HTTP status and code, not the wording of detail.

HTTPCodeMeaning
400cashu-delivery-invalid-requestInvalid delivery identity or metadata.
403cashu-delivery-forbiddenThe authenticated subject cannot access this delivery.
409cashu-delivery-conflictThe request conflicts with saved state, or the delivery was rejected.
400market-funding-invalid-requestInvalid funding amount, unit, or product binding.
404market-funding-market-not-foundThe funding market was not found.
403market-closedThe market is closed to new funding.
500cashu-delivery-state-unavailableThe delivery state could not be read after admission.
502cashu-delivery-recipient-unavailableThe recipient is unavailable.
502cashu-delivery-invalid-receiptThe recipient receipt could not be verified.
404cashu-delivery-not-foundThe status handler could not find the delivery.

Market closure does not itself invalidate an exact retry of a saved delivery. Successful responses retain the saved delivery status. A rejected status read returns 409. A missing status returns 404; another subject’s status returns 403. Route constraints, framework validation, authentication, rate limits, and Score-specific failures can use other error bodies. These codes do not apply to every response with the same HTTP status.

The SDK delivery methods discard error bodies because a recipient can echo a bearer token. They report status-only errors and do not retry automatically. The status method returns null for 404. This SDK behavior is separate from the API’s structured error format.

The engine creates a fill for each matched quantity. fillId identifies one real fill.

The engine groups one or more fills for atomic settlement. groupId identifies one atomic settlement group. A group is not a substitute for a fill, and a fill is not a substitute for a group.

The engine can coalesce compatible fills until the bounded group deadline. It freezes the group before it submits the conversion.

The engine submits one multi-party conversion to the mint for the group. The current product supports complementary and mint conversion. The mint decides the result of that conversion. The NUT defines merge conversion, but bitCaster does not expose it in this release.

The API and settlement notifications use ExpiredBeforeSubmission when authorization expires before submission. RejectedBeforeSubmission means the group stopped before committing a mint request for another reason. Both have a null frozenAt. Neither confirms wallet recovery or authorizes a refund. Clients read the authoritative order status and keep their recovery records if that read fails. Only Confirmed signals confirmed-result recovery.

On confirmation, the mint returns exact result entries for the group. Clients persist the submitted operation and its result. This lets a client recover the exact operation and result after a crash.

Submission can be absent or uncertain after a client or network failure. In that case, the client reconciles with the durable engine and mint authority. It must not infer success from a local request alone. A PAY_TO_UNLOCK capability remains refundable after expiry as defined by the NUT.

An acknowledged FOK operation stores its operation facts and result. These records survive a server restart. An intentional reuse of the same client order ID with the same operation facts returns the stored result. Changed facts return a conflict.

The wallet sends the exact PAY_TO_UNLOCK input proofs that authorize an order. The engine therefore sees those proofs and their secrets. It also sees the public blinded-output manifest. It does not receive the wallet seed, output blinding factors, refund key, or other wallet proofs.

The engine can select only outputs that the wallet authorized. It cannot unblind an output, redirect value outside the manifest, spend another wallet proof, or extend the lock past its expiry. If the engine withholds settlement, the authorized proofs remain unavailable until their refund path becomes valid.

The mint performs the conversion and returns the confirmed result entries. Wallets keep the material that controls proofs and blinding. Nostr authentication identifies an authenticated request through the current adapter. It is not a settlement key exchange and it is not the generic wallet identity.

This product supports sat-denominated assets. It does not provide USD assets. Cashu can provide other features, including HTLC or P2PK conditions, but bitCaster does not use them for its current settlement model.