> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grail.oro.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Quote Buy

> Stateless quote for buying $GOLD with USDC. Returns a partially-signed Solana transaction to co-sign and submit.

## Overview

Builds a buy transaction (USDC → `$GOLD`) and returns it partially-signed by GRAIL. The client must co-sign with the **partner wallet** and the **user wallet**, then either call [Submit Buy](/api-reference/trades/submit-buy) or broadcast directly to a Solana RPC.

This endpoint is stateless — no database row is created at quote time. The `Trade` row is written by the indexer once the transaction confirms on-chain (`confirmed` or `failed`).

<Warning>
  The partial-signed transaction expires in **\~60 seconds** (Solana `recentBlockhash` TTL). If you take too long to co-sign and submit, you'll get `broadcast_failed: Blockhash not found`. Re-quote.
</Warning>

## Headers

<ParamField header="x-api-key" type="string" required>
  A valid `PARTNER` scope key.
</ParamField>

## Request Body

<ParamField body="grail_user_id" type="string" required>
  GRAIL user ID (prefixed `gu_`). User must belong to the authenticated partner, be `active`, and have `kyc_level: "full"`.
</ParamField>

<ParamField body="usdc_amount" type="number" required>
  USDC the user will spend, in human decimal (e.g., `100` = 100 USDC). Must be positive.
</ParamField>

<ParamField body="slippage_bps" type="integer">
  Slippage tolerance in basis points. Default `50` (0.5%). Ignored if `min_gold_out` is provided.
</ParamField>

<ParamField body="min_gold_out" type="number">
  Absolute minimum `$GOLD` to receive (human decimal). If supplied, overrides `slippage_bps`. If omitted, computed as `quoted_gold * (10000 - slippage_bps) / 10000`.
</ParamField>

## Response

<ResponseField name="trade_id" type="string">
  Trade identifier, prefixed `trd_`. Use with [Submit Buy](/api-reference/trades/submit-buy) and [Get Trade](/api-reference/trades/get-trade).
</ResponseField>

<ResponseField name="side" type="string">
  Always `"buy"`.
</ResponseField>

<ResponseField name="quote" type="object">
  <Expandable title="properties">
    <ResponseField name="usdc_amount" type="number">
      Input USDC (echo of request).
    </ResponseField>

    <ResponseField name="gold_amount" type="number">
      Expected `$GOLD` output at quote-time spot price (pre-slippage).
    </ResponseField>

    <ResponseField name="price_per_troy_oz" type="number">
      Gold spot price in USD per troy ounce at quote time.
    </ResponseField>

    <ResponseField name="fee_bps" type="integer">
      Fee rate applied in basis points (derived from the partner's IntegratorV2 on-chain config).
    </ResponseField>

    <ResponseField name="fee_usd" type="number">
      Fee in USDC deducted from the input before swap.
    </ResponseField>

    <ResponseField name="min_gold_out" type="number">
      Minimum `$GOLD` the tx will accept — slippage floor.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="partially_signed_transaction" type="string">
  Base64-encoded Solana transaction, already signed by GRAIL. Co-sign with partner + user and submit.
</ResponseField>

## Errors

| HTTP | `error`                  | When                                                           |
| ---- | ------------------------ | -------------------------------------------------------------- |
| 400  | `invalid_request`        | Missing or non-positive `usdc_amount`, missing `grail_user_id` |
| 400  | `kyc_level_insufficient` | User's KYC level is not `full`                                 |
| 400  | `onchain_config_missing` | Partner's on-chain config hasn't been set up yet. Contact ORO. |
| 400  | `wallet_missing`         | Partner has no registered wallet                               |
| 403  | `partner_mismatch`       | User belongs to a different partner                            |
| 403  | `user_suspended`         | User status is `suspended`                                     |
| 404  | `user_not_found`         | No user with the given `grail_user_id`                         |
| 503  | `pricing_unavailable`    | Gold price oracle unreachable or returned stale data           |

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://grail-stack-dev.onrender.com/v1/buy \
    -H "x-api-key: grail_partner_<hex>" \
    -H "Content-Type: application/json" \
    -d '{
      "grail_user_id": "gu_6b60956e-a8ee-4de2-8128-04c7fdf633c3",
      "usdc_amount": 100,
      "slippage_bps": 50
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 OK theme={null}
  {
    "trade_id": "trd_4e7a1b8f-9c32-4a91-b3e6-7f12a8d4c5e9",
    "side": "buy",
    "quote": {
      "usdc_amount": 100,
      "gold_amount": 0.0205,
      "price_per_troy_oz": 4872.84,
      "fee_bps": 50,
      "fee_usd": 0.50,
      "min_gold_out": 0.0204
    },
    "partially_signed_transaction": "AQAAAAABAAEC...<base64>..."
  }
  ```
</ResponseExample>
