Assetera Docs
API reference

Primary sale API

The three primary-sale rails, the quote lifecycle both on-chain rails share, and the refusal codes an integrator branches on.

A primary sale is a buyer's first acquisition of an asset, and it settles one of three ways. Which one applies is a property of the market, not a choice the caller makes at request time. Read the market first, then call the rail it names.

RailWhat it isWhere the buyer's money goesEndpoints
Bank transfer (offchain_fixed_price)The buyer pays by transfer and the units are delivered once the payment is reconciled.an Assetera client account/offchain-instruments, /offchain-purchases
Third-party venue (onchain_rfq_atomic)A supplier's own settlement contract fills the order atomically on chain.the supplier's contract/rfq/...
Assetera issuance (onchain_primary_mint)The units are minted into existence at purchase, by the offering's own sale contract.the offering's venue contract/primary-mint/...

The bank-transfer rail has its own journey page: Off-chain sale. This page covers the two on-chain rails, which are deliberately the same shape.

How you read the rail

The market carries primaryExecutionKind, and it is on the customer-facing catalog read, not only on the administrative one, precisely so a storefront can route the buyer to the right flow. Branch on it.

primaryExecutionKindRailAlso carried on the market
offchain_fixed_pricebank transfer
onchain_rfq_atomicthird-party venuerfqVenueContract, rfqInstrumentId, and two independent switches, rfqQuotesEnabled and rfqExecutionEnabled
onchain_primary_mintAssetera issuanceissuanceVenueContract
nullthe market has no primary sale at all

Do not infer the rail from primaryMarket

primaryMarket tells you a primary sale exists. It does not tell you how that sale executes, and the three rails need three different sets of calls. A front end that guesses from primaryMarket reproduces one layer up the exact defect this field exists to prevent.

On an onchain_rfq_atomic market the two switches move independently, and the asymmetry is deliberate: quoting off with execution still on is a wind-down that lets an intent already signed by a buyer complete. Both off at once strands it.

The quote lifecycle

Both on-chain rails run the same four calls in the same order. Nothing is signed until step three has persisted a firm quote, and nothing is spent until the buyer submits the transaction themselves.

Step 4 is the last server-side check

Handing over the signed bundle is the final moment Assetera can refuse. Every eligibility rule that applies to a trade is applied here, in the same order: market lifecycle and visibility, the trading window, the buyer's own eligibility axis, appropriateness per asset class, and the trade-size limits. A quote that returned happily at step three can still be refused at step four, and that is correct behaviour rather than a fault.

Endpoints

The two rails mirror each other. Substitute the prefix and everything else is the same shape.

StepAssetera issuanceThird-party venueAuth
Indicative price, binds nothingPOST /primary-mint/quotes/softPOST /rfq/quotes/softtenant + KYC
Is the wallet ready to settlePOST /primary-mint/allowancePOST /rfq/allowancetenant + KYC
Firm quote, persistedPOST /primary-mint/quotesPOST /rfq/quotestenant + KYC
The signed settlement bundlePOST /primary-mint/quotes/:reference/intentPOST /rfq/quotes/:reference/intenttenant + KYC
Read one quote, with its freshnessGET /primary-mint/quotes/:referenceGET /rfq/quotes/:referencetenant
List the caller's own quotesGET /primary-mint/quotesGET /rfq/quotestenant

Exact request and response bodies, including every field name and status code, are in the OpenAPI contract and Swagger UI. This page covers the shape, the rules and the refusals, which the schema cannot express.

Requests, in order

Soft quote. An indicative price. It binds nothing, persists nothing, and is safe to call on every keystroke.

POST /primary-mint/quotes/soft
Authorization: Bearer <access_token>
Content-Type: application/json

{ "pairId": 42, "cashAmount": "100000000" }

Allowance. Asks whether this wallet can actually settle: its balance, and its allowance against the settlement router. Call it before you show a buy button, not after the buyer presses it.

POST /primary-mint/allowance
{ "pairId": 42, "walletAddress": "0x...", "cashAmount": "100000000" }

Firm quote. Persisted before any signature is requested, and it returns a reference. For the issuance rail a reference matches MINT followed by up to 32 hexadecimal characters.

POST /primary-mint/quotes
{ "pairId": 42, "walletAddress": "0x...", "cashAmount": "100000000" }

walletAddress must be registered and screening-approved in Compliance, and it is the only address that can submit the settlement: the router asserts that the intent's buyer equals the meta-transaction sender, so a quote drawn for one wallet cannot be settled by another.

Intent. Exchanges the reference for the signed settlement bundle: the settlement intent, the opaque venue calldata, and the three signatures Assetera produces (the settlement operator's, the compliance attestation and the fee attestation). The buyer then signs the same EIP-712 digest.

POST /primary-mint/quotes/MINT7F3A/intent

This call is idempotent. Asking twice for the same reference returns the same bundle rather than minting a second one, so a retry after a timeout is safe.

Two rules that cause most first-integration failures

Amounts are base units, always

cashAmount is a string of digits only: no decimal point, no sign, no exponent, no 0x prefix. "100000000" is 100 units of a 6-decimal currency, not one hundred million. Never build this value by multiplying a float. See Tokens and pairs for where the decimals come from.

An unknown property is rejected, not ignored

Every request body is validated with additionalProperties: false, so a misspelled or extra field returns 400 instead of being silently dropped. On a route that hands out signatures, a quietly-discarded field would mean a caller believing they asked for something they did not. Do not send fields that are not in the schema, including ones you expect to be added later.

Refusals

Every refusal returns the same envelope, an error code and a human-readable detail. Branch on the code, never on the message text. The codes below are the ones the issuance rail returns. The third-party rail mirrors them under an rfq_ prefix; take its exact list from the OpenAPI contract rather than assuming a one-to-one mapping.

The market is not sellable

CodeWhat it means
mint_market_not_foundNo market with that pairId is visible to your tenant.
mint_not_a_mint_marketThe market exists but settles on a different rail. Read its execution kind and call that rail instead.
mint_market_not_configuredThe market names this rail but its configuration is incomplete. This is an Assetera-side gap, not a caller error.
mint_market_pausedTrading on this market is suspended.

The quote

CodeWhat it means
mint_quote_not_foundNo such reference for this caller. References are caller-scoped.
mint_quote_expiredThe firm quote's freshness window has passed. Take a new quote; do not retry this one.
mint_price_movedThe offering's on-chain price changed between the quote and the intent. Re-quote and show the buyer the new price.
mint_intent_digest_mismatchThe bundle no longer matches the persisted quote. Re-quote.

The buyer or the wallet

CodeWhat it means
mint_wallet_not_registeredThe wallet is not registered and screening-approved for this customer.
mint_kyc_attestation_refusedCompliance declined to attest this settlement. The buyer is not eligible for it.
mint_purchase_cap_exceededThe purchase exceeds the offering's per-purchase cap. Note that a cap of zero means the offering is closed, not unlimited.
mint_nothing_to_mintThe amount rounds down to zero units at this price.

A dependency is unavailable

These are transient and safe to retry with backoff. They are Assetera-side, so do not surface them to the buyer as a rejection.

CodeDependency
mint_price_read_failedThe on-chain price could not be read.
mint_venue_unavailable, mint_venue_mismatchThe offering's venue contract.
mint_signer_unavailable, mint_signer_declinedThe settlement signer.
mint_wallet_registry_unavailableThe wallet registry.
mint_kyc_attestation_unavailableCompliance could not be reached.
mint_kyc_attestation_unsupportedCompliance cannot attest this action on this contract. Configuration, not load.

Next

On this page