Skip to content

Pay for a plan with USDC on Sui ​

You can pay a monthly Inodra plan with USDC on Sui. You do not need a card. Inodra uses Sui native allowances (sui::allowance). You give Inodra a limited right to take the plan price once a month.

Note: This is available on Sui testnet now. Mainnet follows when Sui mainnet enables allowances (protocol version 138, Sui 1.81). Until then, the dashboard shows a "testnet only" note.

Who It Is For ​

  • Teams that hold USDC on Sui and do not want to use a card
  • AI agents and scripts that manage their own plan with a manage key
  • Organizations on the Team, Growth, Professional or Business plan
PlanPrice per month
Team$49
Growth$199
Professional$499
Business$999

Enterprise stays on card. An organization uses one payment method at a time: card or on-chain. To pay per request without a plan, see Pay per request with x402.

How It Works ​

  1. You connect your wallet and choose a plan.
  2. You sign one transaction. It does three things:
    • It moves your prefund (1 to 12 months of USDC) into your USDC address balance.
    • It proposes an allowance for Inodra's billing app. The monthly limit is the plan price.
    • Inodra's billing module issues the allowance.
  3. Inodra takes the first charge within a minute. Your plan is active.
  4. Once a month, Inodra takes the plan price from your USDC address balance.
  5. The on-chain module sends each charge straight to Inodra's treasury.

Inodra never holds your funds and never holds your keys. A leaked Inodra key can only pay Inodra.

What Is an Allowance ​

An allowance is a Sui object that lets one address (the spender) take funds from your address balance. Its rules are on chain:

  • It names the spender. For this feature, the spender is Inodra's billing address.
  • It is bound to one app. Only Inodra's billing module can use it.
  • It has a limit per month. The limit is your plan price.
  • You can revoke it from your wallet at any time.

Set Up From the Dashboard ​

  1. Open Billing.
  2. Select Pay with USDC on Sui.
  3. Connect your wallet.
  4. Choose a plan.
  5. Choose a prefund: 1, 3 or 6 months.
  6. Sign the transaction in your wallet.
  7. Wait for the first charge. It runs within a minute. Then your plan is active.

You pay the gas for this transaction.

Set Up From the API ​

Use a key with the manage scope. See Management API.

1. Read the Plans ​

bash
curl https://mainnet-api.inodra.com/v1/billing/plans \
  -H "x-api-key: indr_pat_..."

The response lists the networks, the spender address, the treasury address, the package ID, the accepted coins and the plan prices.

2. Prepare the Transaction ​

bash
curl -X POST https://mainnet-api.inodra.com/v1/billing/onchain/prepare \
  -H "x-api-key: indr_pat_..." \
  -H "Content-Type: application/json" \
  -d '{ "network": "testnet", "tier": "team", "interval": "month", "funderAddress": "0x...", "prefundPeriods": 3 }'
  • tier is team, growth, professional or business.
  • funderAddress is the wallet that pays. It must sign the transaction.
  • interval is month (default) or year. An annual plan uses a 12-month allowance window and one charge per year at the annual price.
  • prefundPeriods is 0 to 12 periods (months or years) of the plan price. The default is 1.
  • The allowance is named for your organization. Another organization cannot register it.
  • The response holds txBytes: the transaction bytes in base64.

3. Sign and Execute ​

Sign txBytes with the funder's key and execute it with any Sui SDK or wallet. This example uses @mysten/sui v2:

typescript
import { SuiGrpcClient } from '@mysten/sui/grpc'
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519'
import { Transaction } from '@mysten/sui/transactions'
import { fromBase64 } from '@mysten/sui/utils'

const client = new SuiGrpcClient({
  network: 'testnet',
  baseUrl: 'https://fullnode.testnet.sui.io:443'
})
const keypair = Ed25519Keypair.fromSecretKey(process.env.SUI_PRIVATE_KEY!)

const tx = Transaction.from(fromBase64(txBytes))
const bytes = await tx.build({ client })
const { signature } = await keypair.signTransaction(bytes)

const result = await client.core.executeTransaction({
  transaction: bytes,
  signatures: [signature],
  include: { effects: true, objectTypes: true }
})
if (result.$kind !== 'Transaction') throw new Error('Transaction failed')

const { effects, objectTypes } = result.Transaction
const allowanceId = effects.changedObjects.find(
  (o) =>
    o.idOperation === 'Created' && /::allowance::Allowance</.test(objectTypes[o.objectId] ?? '')
)?.objectId

4. Activate the Plan ​

Send the Allowance object ID from the created objects:

bash
curl -X POST https://mainnet-api.inodra.com/v1/billing/onchain/activate \
  -H "x-api-key: indr_pat_..." \
  -H "Content-Type: application/json" \
  -d '{ "network": "testnet", "tier": "team", "allowanceId": "0x..." }'
  • allowanceCapId is optional.
  • Inodra checks the allowance on chain, then takes the first charge.

5. Check the State ​

bash
curl https://mainnet-api.inodra.com/v1/billing/onchain \
  -H "x-api-key: indr_pat_..."

The response shows the state, the plan, the allowance, the funder, the billing balance, the next charge and the last charges.

Renewals and Failed Payments ​

Inodra takes one charge per period (a calendar month, or a year for annual plans), at the end of the period. The charge day is the same day of the month as the first charge. In a short month, Inodra uses the last day. The next month returns to the original day.

Seven days before each renewal, Inodra checks your USDC address balance. If the balance is below the plan price, Inodra emails the owner.

A charge fails when:

  • Your address balance holds less USDC than the plan price.
  • You revoked the allowance.
  • The allowance expired.

After a failed charge:

  1. The plan goes to past_due. Your access continues.
  2. Inodra retries once a day for 7 days.
  3. Inodra emails the owner on attempts 1, 3, 5 and 7.
  4. After the 7th failure, the organization returns to Free. Webhooks and streams above the Free limits pause.

To fix a failed charge, add USDC to your address balance. The next retry succeeds.

A problem on Inodra's side (a fullnode outage, for example) does not count as a failed charge. Inodra retries every hour and does not email you or change the plan state.

StateMeaning
awaiting_first_chargeThe allowance is active. Inodra has not taken the first charge yet
activeThe plan is paid for the current month
past_dueA charge failed. Access continues. Inodra retries once a day for 7 days
cancel_pendingYou cancelled. Access continues until the end of the period
endedThe plan ended. The organization is on Free

Change Your Plan ​

  • Downgrade: The change applies at the next renewal. You do not sign anything. The existing allowance already covers the lower price.
  • Upgrade: You need a new allowance with the higher monthly limit. Inodra registers the new allowance first. Then the dashboard asks you to revoke the old one.

You choose when the change applies:

  • At the next renewal: The new price starts with the next period.
  • Now: Inodra charges the new price minus the unused part of the current month. A new month starts today. The next renewal is the full new price. If this charge fails, your current plan continues and the change applies at the next renewal.

From the API:

bash
curl -X POST https://mainnet-api.inodra.com/v1/billing/onchain/change \
  -H "x-api-key: indr_pat_..." \
  -H "Content-Type: application/json" \
  -d '{ "tier": "growth", "when": "now", "allowanceId": "0x..." }'
  • when is next_renewal or now.
  • For an upgrade, call prepare again with the new tier. Sign and execute the returned bytes. Then send the new allowance ID in allowanceId. Revoke the old allowance from your wallet only after change returns 200. The dashboard does these steps for you.
  • A downgrade with now fails with DOWNGRADE_NEEDS_RENEWAL. Use next_renewal.
  • A cancelled plan cannot be changed. Set it up again with the same plan first (this resumes it).
  • A plan keeps its billing period. To move from monthly to annual, cancel and set up an annual plan after the period ends.
  • change returns 409 CHARGE_IN_PROGRESS while Inodra is taking a charge. Retry in a minute.

Cancel and Revoke ​

  1. Cancel the plan in the dashboard, or call the API:

    bash
    curl -X POST https://mainnet-api.inodra.com/v1/billing/onchain/cancel \
      -H "x-api-key: indr_pat_..."
  2. Your access continues until the end of the period.

  3. Revoke the allowance from your wallet. The dashboard has a Revoke button.

If Inodra finds that you revoked the allowance, it treats this as a cancel at the end of the period.

Plans and Pay-Per-Request Credits ​

An organization is billed one way at a time. A wallet organization that holds pay-per-request credits must spend them before it starts a plan. An organization on a plan cannot receive pay-per-request deposits; use its API key. Prepaid credits as plan overage is planned for a later release.

Billing Balance and Wallet Balance ​

On Sui, an address can hold funds in two forms: Coin objects and an address balance. Your wallet usually shows the total of both. An allowance can only take funds from the address balance. It cannot use your Coin objects. The setup transaction moves your prefund into the address balance. The dashboard shows this amount as the billing balance.

To pay for more months, keep enough USDC in the address balance, or top up before the renewal.

Security ​

  • The allowance limit is the plan price per month. Inodra cannot take more.
  • Only Inodra's billing module can use the allowance. It sends each charge to Inodra's treasury.
  • You can revoke the allowance at any time.
  • Inodra never holds your keys or your funds.

Money Facts ​

ItemValue
CoinUSDC only (6 decimals)
Mainnet USDC0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC
Testnet USDC0xa1ec7fc00a6f40db9693ad1415d0c193ad3906494428cf252621037bd7117e29::usdc::USDC
Gas for pullsInodra pays
Gas for setupYou pay. This includes top-ups and revokes
RefundsNone for a month that started

Error Codes ​

Errors return a JSON body with a code field. The status is 400 unless the table shows another value.

CodeStatusMeaningWhat to do
ONCHAIN_BILLING_DISABLED403On-chain billing is off for this networkUse card billing, or use testnet
PLAN_NOT_AVAILABLE400You cannot pay this plan on chainChoose a monthly paid plan
STRIPE_SUBSCRIPTION_ACTIVE409The organization pays by cardCancel the card plan first
ONCHAIN_SUBSCRIPTION_EXISTS409The organization already has an on-chain planUse change or cancel
ONCHAIN_SUBSCRIPTION_NOT_FOUND404The organization has no on-chain planSet one up first
ALLOWANCE_NOT_FOUND404Inodra cannot find the allowance on chainCheck the ID and the network
ALLOWANCE_WRONG_COIN400The allowance is not for USDCCreate a USDC allowance
ALLOWANCE_WRONG_SPENDER400The allowance does not name Inodra's spenderUse the spender from plans
ALLOWANCE_WRONG_APP400The allowance is not bound to Inodra's appUse the transaction from prepare
ALLOWANCE_WRONG_FUNDER400The funder is not the expected addressSign with funderAddress
ALLOWANCE_WRONG_ORGANIZATION400The allowance was made for another organization or planCall prepare from this organization
ALLOWANCE_IN_USE409The allowance already pays for a planPrepare a new one
PLAN_CANCELLED409The plan is cancelledSet the plan up again to resume it
CHARGE_IN_PROGRESS409Inodra is taking a charge right nowRetry in a minute
INVALID_FUNDER_ADDRESS400funderAddress is not a Sui addressCheck the address
COIN_NOT_ACCEPTED400coinType is not accepted on this networkUse a coin from plans
TX_BUILD_FAILED400The transaction could not be builtRead the message; often a balance
ALLOWANCE_LIMIT_TOO_LOW400The monthly limit is below the plan priceCreate one with a higher limit
ALLOWANCE_EXPIRES_TOO_SOON400The allowance expires before the next chargeCreate one with a later expiry
ALLOWANCE_NOT_STARTED400The allowance is not active yetWait for its start time, then retry
PLAN_UNCHANGED400You asked for the plan that you have, or a cancelled plan with another tier or periodChoose another plan, or resume first
PREPAID_BALANCE_PRESENT409The organization still holds prepaid creditsSpend them before you start a plan
BALANCE_UNAVAILABLE503Inodra could not read the prepaid balanceRetry in a minute
DOWNGRADE_NEEDS_RENEWAL400A downgrade cannot apply nowUse "when": "next_renewal"

FAQ ​

Can I get a refund? No. Inodra does not refund a month that started. When you cancel, access continues until the end of the period.

What if I run out of USDC? The charge fails and the plan goes to past_due. Access continues while Inodra retries once a day for 7 days. Add USDC to your address balance and the next retry succeeds. After 7 failures, the organization returns to Free.

Can Inodra take more than the plan price? No. The allowance limit is the plan price per month. The on-chain module enforces it. The module also sends every charge to Inodra's treasury. You can revoke the allowance at any time.

Who pays the gas? Inodra pays the gas for each monthly charge. You pay the gas for setup, top-ups and revokes.

When is this available on mainnet? When Sui mainnet enables allowances (protocol version 138, Sui 1.81). Until then, it works on testnet only.

The full-stack Sui data layer.