Skip to main content

API-only option quotes

You do not need the trading UI to open an options RFQ. A bot or script posts POST /api/quote with the pair contract, intents (one per leg), and polarity. The API derives per-leg directions from polarity + each intent’s structure, then hashes those directions together with the taker address and a salt. Makers never see the raw taker address — they copy taker_address_hash into the signed offer.

No login is required to create a quote. Login is required only if you later post a maker offer.

For payoff intuition and strike ordering, see Option spreads. For filling as a maker, see Fill options RFQ quotes. On-chain FillMatched / Dutch / Authority: On-chain fill modes. Resting book: CLOB.

Lifecycle (API only)

1. Resolve the pair (CLOB) address for token0 / token1 / premium on the chain
2. POST /api/quote { chain_id, taker_address, polarity, intents, expires_in }
3. Response: quote (id, directions, taker_address_hash, taker_salt, …)
stream_token, topic rfq_stream:<stream_token>
4. Join the Phoenix topic (or poll GET /api/quote/:id with the stream token)
5. Collect rfq_offer events; pick one
6. Wallet: mothership executeIntent(request, makerSignature, maker, directions, takerSalt, takerPermits, referrer)

POST /api/quote is public. GET /api/quote/:id requires stream_token as ?stream_token= or X-Quote-Stream-Token.

Request body

{
"chain_id": 31337,
"taker_address": "0xYourTaker",
"polarity": "b_is_buy",
"expires_in": 60,
"legByLegOnly": true,
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
}
]
}
FieldMeaning
chain_idMust match a registry network (GET /api/networks)
taker_addressWallet that will call executeIntent. Hashed into taker_address_hash; not broadcast on the maker feed
polarity"b_is_buy" = buy / long / bullish the named strategy. "a_is_buy" = sell / short / bearish it
expires_inQuote window in seconds (typically ≤ 300)
legByLegOnly / packageOnly / offer_formatsWhich maker offer shapes to accept
intentsOne shared intent per leg, all on the same opshuns6909_contract

Intent fields

FieldNotes
opshuns6909_contractPair / CLOB address (factory-deployed Opshuns core for that token0–token1–premium triple)
option_type"call" / 0 or "put" / 1
structure0 / "a" follows polarity. 1 / "b" flips that leg
start_exercise_timestamp0 = American (anytime until expiry). Equal to expiry_timestamp = European (expiry day only)
expiry_timestampUnix seconds
strike_priceInteger string in premium token decimals (USDC-6 → 2500 USDC = "2500000000")
amountInteger string in underlying decimals (WETH-18 → 1 WETH = "1000000000000000000"). Butterfly body uses

Polarity, structure, and the address hash

On-chain, a maker offer binds to:

takerAddressHash(address taker, uint256 salt, uint8[] directions)bytes32

(OpshunsRFQ / mothership. Directions: Buy = 0, Sell = 1.)

The API computes directions[i] from quote polarity and intent structure, then hashes them with taker_address and taker_salt. The result is quote.taker_address_hash.

PolarityStructureDirectionTaker on that leg
b_is_buy01 (Sell)Pays premium — long the option
b_is_buy10 (Buy)Posts underlying — short the option
a_is_buy00 (Buy)Posts underlying — short the option
a_is_buy11 (Sell)Pays premium — long the option

Structure 0 follows polarity. Structure 1 flips the leg.

To sell a named strategy, keep the same structure values as the buy recipe below and set "polarity": "a_is_buy". Do not invert structure yourself — polarity already flips every leg’s direction.

Makers must copy taker_address_hash from the RFQ into the signed request exactly. They never receive taker_address.

Shared constants used in the examples

Replace addresses, timestamps, and decimals with live registry / token metadata.

chain_id 31337
opshuns6909_contract 0xPairClob
taker_address 0xYourTaker
expiry_timestamp 1785950335
start_exercise_timestamp 0 # American; use expiry for European
amount (1×) 1000000000000000000 # 1 underlying unit, 18 decimals

Strike strings below assume a 6-decimal premium token.


Single

One call or put. structure: 0 (the only leg follows polarity).

Buy 1 ETH 2500 callpolarity: "b_is_buy"

{
"chain_id": 31337,
"taker_address": "0xYourTaker",
"polarity": "b_is_buy",
"expires_in": 60,
"legByLegOnly": true,
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
}
]
}

Use "option_type": "put" for a put. Sell the same option with "polarity": "a_is_buy" and the same intent.

Derived direction for this buy call: [1] (taker pays premium). taker_address_hash = takerAddressHash(taker, salt, [1]).


Straddle

Long call + long put at the same strike. Both legs structure: 0.

Buy 2500 straddle

"polarity": "b_is_buy",
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "put",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
}
]

Buy directions: [1, 1]. Sell (a_is_buy): same intents, directions [0, 0].


Strangle

OTM put + OTM call at different strikes. Put strike below call strike. Both long when buying → structure: 0.

Buy 2000 / 3000 strangle

"polarity": "b_is_buy",
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "put",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2000000000",
"amount": "1000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "3000000000",
"amount": "1000000000000000000"
}
]

Butterfly

Three strikes of the same type, ascending: low < mid < high. Buying: long wings (structure: 0), short body (structure: 1).

Buy 2000 / 2500 / 3000 call butterfly (put butterfly: "option_type": "put" on every leg)

"polarity": "b_is_buy",
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2000000000",
"amount": "1000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 1,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "2000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "3000000000",
"amount": "1000000000000000000"
}
]

Buy directions: [1, 0, 1] (long, short, long). The middle amount is twice the 1× size.


Condor

Four strikes of the same type: outer low < inner low < inner high < outer high. Buying: long outer wings (0), short inner strikes (1). All amounts 1×.

Buy call condor 1800 / 2200 / 2800 / 3200

"polarity": "b_is_buy",
"intents": [
{ "option_type": "call", "structure": 0, "strike_price": "1800000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2200000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2800000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3200000000", "amount": "1000000000000000000" }
]

(Each object also needs opshuns6909_contract, start_exercise_timestamp, and expiry_timestamp as in the full examples above.)

Buy directions: [1, 0, 0, 1].


Iron butterfly

Long OTM put wing, short ATM put, short ATM call, long OTM call wing. Body strike is shared. Order: put wing < body < call wing.

Buying the named iron butterfly is the debit (long wings, short body). structure: 1 on the two short body legs.

Buy iron butterfly (put wing 2000, body 2500, call wing 3000)

"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 0, "strike_price": "2000000000", "amount": "1000000000000000000" },
{ "option_type": "put", "structure": 1, "strike_price": "2500000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2500000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3000000000", "amount": "1000000000000000000" }
]

Buy directions: [1, 0, 0, 1]. Sell (a_is_buy) is the credit iron butterfly (short wings, long body) with the same structures.


Iron condor

Long far put, short inner put, short inner call, long far call. Order: long put < short put < short call < long call.

Buy iron condor (1800 / 2200 / 2800 / 3200)

"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 0, "strike_price": "1800000000", "amount": "1000000000000000000" },
{ "option_type": "put", "structure": 1, "strike_price": "2200000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2800000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3200000000", "amount": "1000000000000000000" }
]

Buy directions: [1, 0, 0, 1].


Risk reversal

Bullish: long call + short put. Bearish: long put + short call (same intents, polarity: "a_is_buy"). Put strike below call strike.

The short put is the flipped leg when buying/bullish → structure: 1. The long call follows polarity → structure: 0.

Bullish risk reversal (put 2000, call 3000)

"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 1, "strike_price": "2000000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3000000000", "amount": "1000000000000000000" }
]

Buy/bullish directions: [0, 1] (short put, long call).


Collar

Long collar: long put + short call (classic protective collar). Short collar: reverse via a_is_buy. Put strike below call strike.

Long collar (put 2000, call 3000)

"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 0, "strike_price": "2000000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "3000000000", "amount": "1000000000000000000" }
]

Buy/long directions: [1, 0] (long put, short call).


Structure cheat sheet (buy / long / bullish)

polarity is always "b_is_buy" in this table. Selling the same strategy: same rows, "polarity": "a_is_buy".

StrategyLegs (option_type, structure, amount)
Single(call or put, 0, 1×)
Straddle(call, 0, 1×), (put, 0, 1×) same strike
Strangle(put, 0, 1×), (call, 0, 1×) put < call
Butterfly(type, 0, 1×) low, (type, 1, ) mid, (type, 0, 1×) high
Condor(type, 0, 1×) outer low, (type, 1, 1×) inner low, (type, 1, 1×) inner high, (type, 0, 1×) outer high
Iron butterfly(put, 0, 1×) wing, (put, 1, 1×) body, (call, 1, 1×) body, (call, 0, 1×) wing
Iron condor(put, 0, 1×) long, (put, 1, 1×) short, (call, 1, 1×) short, (call, 0, 1×) long
Risk reversal(put, 1, 1×), (call, 0, 1×)
Collar(put, 0, 1×), (call, 1, 1×)

After create

201 body includes:

  • quote.directionsuint8[] used on-chain and in the hash
  • quote.taker_address_hashtakerAddressHash(taker_address, taker_salt, directions)
  • quote.taker_salt
  • stream_token / topic — private offer stream
curl -sS https://api.staging.opshuns.com/api/quote \
-H 'Content-Type: application/json' \
-d @quote.json

Then subscribe:

Phoenix /socket → join rfq_stream:<stream_token>
events: rfq_offer, rfq_result

Or poll:

GET /api/quote/<id>
X-Quote-Stream-Token: <stream_token>

Settlement still uses the quote’s directions and taker_salt in executeIntent. Approve the mothership (or factory pull spender) for premium and/or underlying according to those directions before sending the transaction.

The trading UI’s post to book path (single calls/puts after an empty RFQ window) does not use this endpoint — it posts an unfilled order on the pair CLOB. See Orders & exclusive fills and Trading app.

Dutch auction (dutch_only)

Create a taker-signed Dutch competition instead of a standard RFQ. Same POST /api/quote endpoint; detection is via offer_formats: "dutch_only" (or a present dutch_request).

{
"chain_id": 31337,
"taker_address": "0xYourTaker",
"offer_formats": "dutch_only",
"directions": [1],
"expires_in": 300,
"compete_ms": 30000,
"exclusive_seconds": 5,
"taker_signature": "0x…",
"dutch_request": {
"intents": [/* same intent shape as standard RFQ */],
"tokens": [{ "token0": "0x…", "token1": "0x…", "premium_token": "0x…" }],
"dutch_premium": {
"token": "0x…",
"maker_pays_taker": false,
"start_premium": "1000000",
"end_premium": "500000",
"decay_rate": "1667",
"auction_start": 1785950000
},
"auction_end": 1785950300,
"maker_address_hash": "0x…",
"taker_nonce": "1785950000123456",
"required_authority": "0x0000000000000000000000000000000000000000",
"required_dutch_authority": "0xMothership",
"referrer": "0x0000000000000000000000000000000000000000"
}
}
FieldMeaning
directionsExplicit Buy/Sell per leg (revealed to the API; not broadcast to makers while dutch_phase is competing)
taker_signatureEIP-712 signature over mothership digestDutchFillRequest (domain OpshunsDutchFill / 1) recovering to taker_address
dutch_requestNormalized on-chain DutchFillRequest (snake_case). Intents are taken from here
compete_msOptional competition window (clamped to auction TTL)
exclusive_ms / exclusive_secondsOptional exclusive award window (admin max applies)

Competition binding (auctioneer path)

For API-brokered competition (what the trading UI uses):

  • maker_address_hash = directionsHash(directions) (polarity-only; not a bound maker)
  • required_dutch_authority = mothership (any registered dutch auctioneer)
  • required_authority = 0 unless you also require KYC fill-authority

Signing reveals trade side because directions are committed into the hash and posted with the quote.

Phases

dutch_phaseBehavior
competingPublic maker feed hides directions / executable payload; makers POST both-side bids on POST /api/quote/:id/offer (buy_override_bps, sell_override_bps)
exclusiveAuctioneer awards a winner; fillable payload published; preferred maker receives private exclusive_salt
openSame public fillable; any eligible maker may fill (soft-override / sniping rules apply)

Taker stream (rfq_stream:<stream_token>) receives rfq_offer for dutch bids and rfq_request / poll updates when the phase advances. On-chain settlement: On-chain fill modes — Dutch. Trading UI: Dutch auction (UI).

Resolve the pair contract

opshuns6909_contract is the deployed pair for (token0, token1, premiumToken) on that chain — not the underlying ERC-20. Discover factory / token addresses from GET /api/networks, then read the pair from the factory (or create it first if it is not deployed). All legs of a spread must use the same pair address and the same expiry.