> ## Documentation Index
> Fetch the complete documentation index at: https://tetrafi.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Order Submission

> Have TetraFi land the settlement transaction - your users sign once and never touch gas.

On an `apiSubmit` quote, your user signs the order payload and TetraFi puts the settlement on-chain. No gas, no RPC, no broadcast logic - the right default for wallets and super-apps where UX comes first.

There is no last-look trade-off here: every RFQ quote is already a firm, signed commitment, and settlement runs delivery-versus-payment through the escrow. What changes with `apiSubmit` is only *who* lands the transaction.

<Info>
  **Reach for this when** your users should never encounter gas at all - the wallet and super-app case. Teams operating their own submission infrastructure generally take `walletBroadcast` instead; the [quickstart](/rfq-api/quickstart) covers that route.
</Info>

## The Two Paths Side by Side

|                  | walletBroadcast                   | apiSubmit                                        |
| ---------------- | --------------------------------- | ------------------------------------------------ |
| Transaction path | Your wallet and RPC carry it      | TetraFi lands it on-chain                        |
| Order endpoint   | Prepared tx arrives via preflight | POST the signed order to `/api/v1/rfq/orders`    |
| Firmness         | Firm - a signed escrow commitment | Firm - the identical guarantee, no last look     |
| Gas              | On the taker                      | Invisible to the user                            |
| Funding          | Straight from the wallet          | Escrow lock: Permit2, EIP-3009, or resource lock |

## 1. Get a Quote You Can Submit

Request quotes as usual - the execution mode rides on each quote, so there's no request flag to set:

```python theme={null} theme={null}
import httpx

API = "https://api.tetrafi.io/api/v1"
ME = "0xYourWalletAddress"

resp = httpx.post(
    f"{API}/rfq/quotes",
    json={
        "user": ME,
        "intent": {
            "intentType": "swap",
            "inputs": [{
                "user": ME,
                "asset": {"chainId": 10, "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"},
                "amount": "25000000",
            }],
            "outputs": [{
                "receiver": ME,
                "asset": {"chainId": 8453, "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},
            }],
            "swapType": "ExactInput",
        },
        "supportedTypes": ["escrow-v0"],
    },
    headers={"X-API-Key": "tfk_live_..."},
)
quotes = resp.json()["quotes"]
```

### How Your Funds Reach Escrow

Instead of approval flags on the request, the funding mechanism is a property of the corridor and token - preflight selects it and tells you what (if anything) to set up:

| Lock          | How it works                                                           | Setup                 |
| ------------- | ---------------------------------------------------------------------- | --------------------- |
| Permit2       | Escrow pull authorized inside your signature                           | One reusable approval |
| EIP-3009      | `transferWithAuthorization` baked into the signed message (USDC-style) | None                  |
| Resource lock | Order draws on a pre-deposited compact-settler balance                 | Prior deposit         |

<Tip>
  **Which lock will I get?** You don't choose - run preflight and read the `nextActions`. A `tokenApproval` action with `frequency: "reusableSetup"` is your one-time Permit2 grant; see [Using Permit2 Approvals](#using-permit2-approvals) below.
</Tip>

## 2. Sign the Order Payload

Preflight your chosen quote; its `orderSignature` action carries the complete EIP-712 envelope. Sign it exactly as returned - domain, types, and message all arrive assembled:

```python theme={null} theme={null}
from eth_account import Account
from eth_account.messages import encode_typed_data

PRIVATE_KEY = "0x<your_private_key_hex>"
headers = {"X-API-Key": "tfk_live_..."}

selected = quotes[0]

pf = httpx.post(f"{API}/rfq/orders/preflight", headers=headers,
                json={"quoteResponse": selected}).json()

sign_action = next(a for a in pf["nextActions"] if a["purpose"] == "orderSignature")

signable = encode_typed_data(full_message=sign_action["typedData"])
signature = Account.sign_message(signable, private_key=PRIVATE_KEY).signature.hex()
```

The payload's shape adapts to the trade's structure automatically - you never pick a type by hand:

<Accordion title="Inside the escrow-v0 payload">
  | Trade shape           | What the order encodes                                                    |
  | --------------------- | ------------------------------------------------------------------------- |
  | One input, one output | A single escrowed input against a single enforced delivery                |
  | Multi-token           | Several inputs and/or outputs settled atomically in one order             |
  | Split fill            | Multiple LPs each committed to a slice, settled under one taker signature |

  Whatever the shape, the envelope always binds: the funding wallet and receivers, the escrowed amounts and minimum deliveries, an expiry (after which the escrow refunds instead of settling), a replay-protection nonce, and the terms your `integrityChecksum` locks to your submission.

  Sign the structure verbatim - anything you'd want different (receiver, amounts, expiry) is decided at quote time, never at signing time.
</Accordion>

## 3. Submit It

```
POST /api/v1/rfq/orders
```

```python theme={null} theme={null}
order = httpx.post(
    f"{API}/rfq/orders",
    json={"quoteResponse": selected, "signature": signature},
    headers={**headers, "Idempotency-Key": selected["quoteId"]},
).json()
print("Order:", order["id"])
```

Submission fields:

| Field             | Type   | Description                                                                                        |
| ----------------- | ------ | -------------------------------------------------------------------------------------------------- |
| `quoteResponse`   | object | The chosen quote, passed back unchanged                                                            |
| `signature`       | string | Your EIP-712 signature (hex). EOAs sign ECDSA; smart-contract wallets verify via EIP-1271 on-chain |
| `Idempotency-Key` | header | Makes retries safe - the same key can never double-submit                                          |

## 4. Follow It to Settlement

```python theme={null} theme={null}
import time

while True:
    o = httpx.get(f"{API}/rfq/orders/{order['id']}", headers=headers).json()
    print("status:", o["status"])
    if o["status"] in ("settled", "failed"):
        break
    time.sleep(2)
```

Or subscribe to `orders:{orderId}` on `wss://api.tetrafi.io/api/v1/ws?token=<key>` for push updates:

| Event / state      | Meaning                                                  |
| ------------------ | -------------------------------------------------------- |
| `order.created`    | Accepted - funds are locking and settlement is in motion |
| `order.settled`    | Delivery proven; escrow released - the trade is complete |
| `order.failed`     | Could not settle - the escrow refunds your locked input  |
| `compliance.event` | A compliance checkpoint fired during processing          |

A failed `apiSubmit` order never strands funds: the delivery-or-refund escrow returns your input if delivery can't be proven before expiry.

## Full Example

```python theme={null} theme={null}
import time

import httpx
from eth_account import Account
from eth_account.messages import encode_typed_data

# --- Config ---
API = "https://api.tetrafi.io/api/v1"
KEY = "tfk_live_..."
PRIVATE_KEY = "0x<your_private_key_hex>"
ME = "0x2e7E7cc62919eAf4c502dAC34753cFc5A29e9693"

headers = {"X-API-Key": KEY}
account = Account.from_key(PRIVATE_KEY)

# --- 1. Quotes ---
quotes = httpx.post(f"{API}/rfq/quotes", headers=headers, json={
    "user": ME,
    "intent": {
        "intentType": "swap",
        "inputs": [{"user": ME,
                    "asset": {"chainId": 10, "address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"},
                    "amount": "25000000"}],
        "outputs": [{"receiver": ME,
                     "asset": {"chainId": 8453, "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"}}],
        "swapType": "ExactInput",
    },
    "supportedTypes": ["escrow-v0"],
}).json()["quotes"]

selected = quotes[0]
print("Best quote from:", selected["solverId"])

# --- 2. Preflight ---
pf = httpx.post(f"{API}/rfq/orders/preflight", headers=headers,
                json={"quoteResponse": selected}).json()

# --- 3. Run setup actions (approvals), if any ---
#   tokenApproval actions arrive as ready-to-broadcast transactions;
#   see the Token Approvals guide for the loop.

# --- 4. Sign ---
sign_action = next(a for a in pf["nextActions"] if a["purpose"] == "orderSignature")
signable = encode_typed_data(full_message=sign_action["typedData"])
signature = Account.sign_message(signable, private_key=PRIVATE_KEY).signature.hex()

# --- 5. Submit ---
order = httpx.post(f"{API}/rfq/orders",
                   headers={**headers, "Idempotency-Key": selected["quoteId"]},
                   json={"quoteResponse": selected, "signature": signature}).json()
print("Order:", order["id"])

# --- 6. Track ---
while True:
    o = httpx.get(f"{API}/rfq/orders/{order['id']}", headers=headers).json()
    print("status:", o["status"])
    if o["status"] in ("settled", "failed"):
        break
    time.sleep(2)
```

## Using Permit2 Approvals

Permit2 turns per-settler approvals into one reusable grant: approve the canonical [Permit2 contract](https://github.com/Uniswap/permit2) once per token, and every subsequent order authorizes its own escrow pull inside the signature you were already making.

How it shows up in practice:

* **First trade on a token:** preflight returns a `tokenApproval` action (`frequency: "reusableSetup"`) - a prepared transaction granting Permit2 a bounded allowance. Broadcast it once.
* **Every trade after:** no approval action appears; the order's signed payload itself carries the pull authorization. One signature, zero extra transactions.
* **USDC-style tokens:** may skip approvals entirely - EIP-3009 `transferWithAuthorization` authorizes the transfer inside the message, so preflight simply won't list a setup step.

```python theme={null} theme={null}
# The Permit2 setup step, when preflight requests it:
setup = [a for a in pf["nextActions"]
         if a["purpose"] == "tokenApproval" and a["frequency"] == "reusableSetup"]

for action in setup:
    tx = action["tx"]  # prepared {to, data, value} - broadcast once, reuse forever
    ...
```

<Note>
  Bounded, reusable grants (rather than infinite approvals to many contracts) are the point: your exposure is one audited contract, sized allowances, and signatures that expire with their orders.
</Note>

<Info>
  After the one-time setup, subsequent trades on the same token are pure sign-and-submit - the fastest path from quote to settlement the RFQ API offers.
</Info>
