Misar IO Docs

Idempotency & concurrency

How the Universal Wallet keeps deductions race-free and retries safe.

The wallet is designed so that retries and concurrent calls can never overcharge or double-credit a user.

Atomic deductions

POST /io/wallet/deduct performs the balance check and the debit as a single atomic operation in the ledger. Two requests racing to spend the last credit cannot both succeed — one is debited and the other returns allowed: false. You never need to read-then-write the balance yourself; doing so would introduce a race the atomic deduct already avoids.

Idempotent deductions

Pass a stable idempotency_key whenever the same logical charge might be retried — a network blip, a queue redelivery, or a user double-click.

curl -X POST "https://api.misar.io/io/wallet/deduct" \
  -H "x-wallet-service-key: $WALLET_SERVICE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "usr_123",
    "feature": "blog.article.generate",
    "idempotency_key": "article:my-post-slug"
  }'

Re-sending the same idempotency_key returns the original result and does not charge again.

Pick a key tied to the unit of work

Good keys are deterministic from the work itself — an article slug, a job id, a message id. Avoid random keys generated per attempt; a fresh key on each retry defeats the protection.

Idempotent top-ups

Credit grants are keyed on the Stripe checkout session id. Stripe may deliver checkout.session.completed more than once; the webhook credits a given session exactly once. Redelivery is a no-op.

Best practices

Deduct before the work

Charge first, then run the billable feature only if allowed is true.

Use a deterministic idempotency key

Derive it from the unit of work so all retries collapse to one charge.

Never read-modify-write the balance

Rely on the atomic deduct — do not fetch the balance, subtract, and write it back.