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

# Instruction Flow

> Submit instructions, share trade references, and track confirmed settlements.

<Warning>
  **Not yet released.** The packages on this page are not published to npm yet, so the install commands below will not resolve. The documented interface is stable and the code exists; only publication is outstanding. To use KeyStone from a frontend today, call the [REST API](/api-reference/overview) or the [TypeScript SDK](/sdks/typescript/getting-started), which is published. Contact us if you need early access.
</Warning>

This guide walks through building the instruction flow - where a seller submits their side of a trade, shares a trade reference with the buyer, and the buyer submits the counterpart instruction to create a settlement.

## Prerequisites

* `@keystoneos/react` installed
* `KeystoneProvider` configured with a session token that has `settlements:write` and `templates:read` scopes, and no `settlementIds` restriction (an instruction has no settlement yet)
* A React project with TypeScript

## How settlement works

In a paired flow, two independent parties agree on a trade off-platform (via phone, email, or OMS). Each party submits their side of the pre-agreed trade to KeyStone. KeyStone compares the two sides on instrument, direction and quantity, and creates the settlement only if they agree. It does not bring together buying and selling interests: the parties selected each other upstream and both present a shared trade reference. KeyStone does not price, net, allocate, or take custody.

```mermaid theme={null}
sequenceDiagram
    participant S as Seller (Securitize Fund I)
    participant SP as Seller's Platform
    participant KS as KeyStone API
    participant BP as Buyer's Platform
    participant B as Buyer (Ondo Capital)

    S->>SP: Enters trade details
    SP->>KS: POST /v1/instructions (seller side)
    KS-->>SP: status: pending_match, tradeReference: KS-abc12345

    S-->>B: Shares trade reference (email, chat, OMS)

    B->>BP: Enters trade details + trade reference
    BP->>KS: POST /v1/instructions (buyer side, tradeReference: KS-abc12345)
    KS->>KS: Counterpart found - instructions agree,<br/>create settlement
    KS-->>BP: status: matched, settlementId: 550e8400-...

    KS-->>SP: Webhook: settlement.state.awaiting_deposits<br/>(once compliance clears)
```

The key points:

* The **first** party to submit gets `status: "pending_match"` and a trade reference
* The **second** party submits with that trade reference and gets `status: "matched"` plus a settlement ID
* Either party can be the first to submit - there is no required order

<Note>
  The `pending_match` / `matched` statuses describe reference correlation of a pre-agreed trade, not order matching - see [what "matched" means](/guides/submitting-instructions#what-matched-means).
</Note>

## Understanding legs

A DvP (Delivery vs Payment) settlement involves two legs:

| Leg Type         | Description                      | Seller   | Buyer    |
| ---------------- | -------------------------------- | -------- | -------- |
| `asset_delivery` | The tokenized asset being traded | Delivers | Receives |
| `payment`        | The payment (e.g., USDC)         | Receives | Delivers |

Each party submits **both legs** in their instruction. The direction is derived automatically from the role:

```
seller + asset_delivery = deliver
seller + payment        = receive
buyer  + asset_delivery = receive
buyer  + payment        = deliver
```

Each leg specifies its own `chain_id` and `instrument_id` (the token contract address).

## Building the instruction form

### Template selector

Start by letting the user pick a settlement template. Templates define the structure of the settlement (roles, leg types, state machine).

```tsx theme={null}
// template-selector.tsx
import { useTemplates } from '@keystoneos/react';

interface TemplateSelectorProps {
  value: string | undefined;
  onChange: (slug: string) => void;
}

export function TemplateSelector({ value, onChange }: TemplateSelectorProps) {
  const { templates, isLoading } = useTemplates();

  if (isLoading) return <div>Loading templates...</div>;

  return (
    <div>
      <label style={{ display: 'block', marginBottom: '4px', fontWeight: 600, fontSize: '14px' }}>
        Settlement Template
      </label>
      <select
        value={value ?? ''}
        onChange={e => onChange(e.target.value)}
        style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db' }}
      >
        <option value="" disabled>Select a template</option>
        {templates.map(t => (
          <option key={t.id} value={t.slug}>
            {t.name} - Roles: {t.config.required_roles.join(', ')}
          </option>
        ))}
      </select>
    </div>
  );
}
```

### Trade reference input

When confirming against an existing instruction, the user provides the trade reference they received from the counterparty.

```tsx theme={null}
// trade-reference-input.tsx
interface TradeReferenceInputProps {
  value: string;
  onChange: (value: string) => void;
}

export function TradeReferenceInput({ value, onChange }: TradeReferenceInputProps) {
  return (
    <div>
      <label style={{ display: 'block', marginBottom: '4px', fontWeight: 600, fontSize: '14px' }}>
        Trade Reference (optional)
      </label>
      <input
        type="text"
        value={value}
        onChange={e => onChange(e.target.value)}
        placeholder="e.g. KS-abc12345 - leave empty to create new"
        style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db' }}
      />
      <p style={{ fontSize: '12px', color: '#6b7280', marginTop: '4px' }}>
        If confirming against an existing instruction, enter the trade reference shared by your counterparty.
      </p>
    </div>
  );
}
```

### The complete instruction form

This form collects role, party details, template-driven legs, and optional trade reference. Each template defines which leg types are required - for DvP, that is `asset_delivery` and `payment`.

```tsx theme={null}
// instruction-form.tsx
import { useState } from 'react';
import { useSubmitInstruction, useTemplates } from '@keystoneos/react';
import { TemplateSelector } from './template-selector';
import { TradeReferenceInput } from './trade-reference-input';
import { PendingMatchResult } from './pending-match-result';
import { MatchedResult } from './matched-result';

// Client-side map of template slug to the leg types your UI should collect.
// The API template config does not enumerate leg types, so keep this list
// in your own frontend config.
const LEG_TYPES_BY_TEMPLATE: Record<string, string[]> = {
  'cross_platform_dvp': ['asset_delivery', 'payment'],
  'single_platform_dvp': ['asset_delivery', 'payment'],
};

const LEG_TYPE_LABELS: Record<string, string> = {
  asset_delivery: 'Asset Delivery',
  payment: 'Payment',
};

function deriveDirection(role: string, legType: string): 'deliver' | 'receive' {
  if ((role === 'seller' && legType === 'asset_delivery') ||
      (role === 'buyer' && legType === 'payment')) {
    return 'deliver';
  }
  return 'receive';
}

interface LegState {
  instrumentId: string;
  quantity: string;
  chainId: number;
}

interface FormData {
  role: 'seller' | 'buyer';
  partyReference: string;
  partyName: string;
  walletAddress: string;
  templateSlug: string;
  tradeReference: string;
  legs: Record<string, LegState>;
}

function buildInitialLegs(legTypes: string[]): Record<string, LegState> {
  return Object.fromEntries(
    legTypes.map(lt => [lt, { instrumentId: '', quantity: '', chainId: 11155111 }])
  );
}

export function InstructionForm() {
  const defaultLegTypes = ['asset_delivery', 'payment'];
  const [form, setForm] = useState<FormData>({
    role: 'seller',
    partyReference: '',
    partyName: '',
    walletAddress: '',
    templateSlug: '',
    tradeReference: '',
    legs: buildInitialLegs(defaultLegTypes),
  });
  const { submit, result, isSubmitting, error, reset } = useSubmitInstruction();

  const legTypes = LEG_TYPES_BY_TEMPLATE[form.templateSlug] ?? defaultLegTypes;

  const handleTemplateChange = (slug: string) => {
    const newLegTypes = LEG_TYPES_BY_TEMPLATE[slug] ?? defaultLegTypes;
    setForm(prev => ({ ...prev, templateSlug: slug, legs: buildInitialLegs(newLegTypes) }));
  };

  const updateLeg = (legType: string, field: keyof LegState, value: string | number) => {
    setForm(prev => ({
      ...prev,
      legs: { ...prev.legs, [legType]: { ...prev.legs[legType], [field]: value } },
    }));
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    await submit({
      role: form.role,
      party: {
        external_reference: form.partyReference,
        name: form.partyName,
        wallet_address: form.walletAddress,
      },
      legs: legTypes.map(lt => ({
        leg_type: lt,
        instrument_id: form.legs[lt].instrumentId,
        quantity: form.legs[lt].quantity,
        direction: deriveDirection(form.role, lt),
        chain_id: form.legs[lt].chainId,
      })),
      templateSlug: form.templateSlug,
      timeoutAt: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
      tradeReference: form.tradeReference || undefined,
    });
  };

  const handleReset = () => {
    reset();
    setForm({
      role: 'seller',
      partyReference: '',
      partyName: '',
      walletAddress: '',
      templateSlug: '',
      tradeReference: '',
      legs: buildInitialLegs(defaultLegTypes),
    });
  };

  // Show result screens after submission
  if (result?.status === 'pending_match') {
    return <PendingMatchResult result={result} onReset={handleReset} />;
  }

  if (result?.status === 'matched') {
    return <MatchedResult result={result} />;
  }

  return (
    <form onSubmit={handleSubmit} style={{ maxWidth: '600px', display: 'flex', flexDirection: 'column', gap: '20px' }}>
      <h2 style={{ fontSize: '20px', fontWeight: 700 }}>Submit Settlement Instruction</h2>

      {/* Role selection */}
      <div>
        <label style={{ display: 'block', marginBottom: '4px', fontWeight: 600, fontSize: '14px' }}>Role</label>
        <div style={{ display: 'flex', gap: '12px' }}>
          {(['seller', 'buyer'] as const).map(role => (
            <label key={role} style={{ display: 'flex', alignItems: 'center', gap: '6px', cursor: 'pointer' }}>
              <input
                type="radio"
                name="role"
                value={role}
                checked={form.role === role}
                onChange={() => setForm(prev => ({ ...prev, role }))}
              />
              <span style={{ textTransform: 'capitalize' }}>{role}</span>
            </label>
          ))}
        </div>
      </div>

      {/* Party details */}
      <fieldset style={{ border: '1px solid #e5e7eb', borderRadius: '8px', padding: '16px' }}>
        <legend style={{ fontWeight: 600, fontSize: '14px', padding: '0 8px' }}>Party Details</legend>
        <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
          <div>
            <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>Account Reference</label>
            <input
              type="text"
              value={form.partyReference}
              onChange={e => setForm(prev => ({ ...prev, partyReference: e.target.value }))}
              placeholder="e.g. KSFI-II-4401"
              required
              style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db' }}
            />
          </div>
          <div>
            <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>Display Name</label>
            <input
              type="text"
              value={form.partyName}
              onChange={e => setForm(prev => ({ ...prev, partyName: e.target.value }))}
              placeholder="e.g. Securitize Fund I"
              style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db' }}
            />
          </div>
          <div>
            <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>Wallet Address</label>
            <input
              type="text"
              value={form.walletAddress}
              onChange={e => setForm(prev => ({ ...prev, walletAddress: e.target.value }))}
              placeholder="e.g. 0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18"
              required
              style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db', fontFamily: 'monospace', fontSize: '13px' }}
            />
          </div>
        </div>
      </fieldset>

      {/* Legs - template driven */}
      <fieldset style={{ border: '1px solid #e5e7eb', borderRadius: '8px', padding: '16px' }}>
        <legend style={{ fontWeight: 600, fontSize: '14px', padding: '0 8px' }}>
          Settlement Legs
        </legend>
        <p style={{ fontSize: '12px', color: '#6b7280', marginBottom: '16px' }}>
          Each leg has its own token address and chain. Direction is auto-derived from your role.
        </p>
        {legTypes.map((lt, index) => (
          <div key={lt}>
            {index > 0 && <hr style={{ border: 'none', borderTop: '1px dashed #e5e7eb', margin: '16px 0' }} />}
            <div style={{ marginBottom: '12px' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
                <span style={{
                  padding: '2px 10px',
                  borderRadius: '9999px',
                  fontSize: '11px',
                  fontWeight: 600,
                  textTransform: 'uppercase',
                  background: lt === 'asset_delivery' ? 'rgba(22,163,74,0.1)' : 'rgba(37,99,235,0.1)',
                  color: lt === 'asset_delivery' ? '#16a34a' : '#2563eb',
                  border: lt === 'asset_delivery' ? '1px solid rgba(22,163,74,0.3)' : '1px solid rgba(37,99,235,0.3)',
                }}>
                  {LEG_TYPE_LABELS[lt] ?? lt}
                </span>
                <span style={{ fontSize: '12px', color: '#6b7280', textTransform: 'capitalize' }}>
                  {deriveDirection(form.role, lt) === 'deliver'
                    ? `${form.role} delivers to ${form.role === 'seller' ? 'buyer' : 'seller'}`
                    : `${form.role === 'seller' ? 'buyer' : 'seller'} delivers to ${form.role}`}
                </span>
              </div>
              <div style={{ display: 'flex', gap: '12px' }}>
                <div style={{ flex: 1 }}>
                  <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>Token Address</label>
                  <input
                    type="text"
                    value={form.legs[lt]?.instrumentId ?? ''}
                    onChange={e => updateLeg(lt, 'instrumentId', e.target.value)}
                    placeholder="0x..."
                    required
                    style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db', fontFamily: 'monospace', fontSize: '13px' }}
                  />
                </div>
                <div style={{ flex: 1 }}>
                  <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>Quantity</label>
                  <input
                    type="text"
                    value={form.legs[lt]?.quantity ?? ''}
                    onChange={e => updateLeg(lt, 'quantity', e.target.value)}
                    placeholder="e.g. 12000000"
                    required
                    style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db', fontFamily: 'monospace' }}
                  />
                </div>
                <div style={{ width: '160px' }}>
                  <label style={{ display: 'block', marginBottom: '4px', fontSize: '13px' }}>Chain ID</label>
                  <select
                    value={form.legs[lt]?.chainId ?? 11155111}
                    onChange={e => updateLeg(lt, 'chainId', Number(e.target.value))}
                    style={{ width: '100%', padding: '8px 12px', borderRadius: '6px', border: '1px solid #d1d5db' }}
                  >
                    <option value={1}>Ethereum (1)</option>
                    <option value={137}>Polygon (137)</option>
                    <option value={43114}>Avalanche (43114)</option>
                    <option value={11155111}>Sepolia (11155111)</option>
                  </select>
                </div>
              </div>
            </div>
          </div>
        ))}
      </fieldset>

      {/* Template and trade reference */}
      <TemplateSelector value={form.templateSlug || undefined} onChange={handleTemplateChange} />
      <TradeReferenceInput value={form.tradeReference} onChange={val => setForm(prev => ({ ...prev, tradeReference: val }))} />

      {/* Submit */}
      {error && (
        <div style={{ padding: '12px', backgroundColor: '#fee2e2', color: '#991b1b', borderRadius: '6px', fontSize: '14px' }}>
          {error}
        </div>
      )}

      <button
        type="submit"
        disabled={isSubmitting || !form.templateSlug}
        style={{
          padding: '12px 24px',
          backgroundColor: isSubmitting ? '#9ca3af' : '#2DD4A8',
          color: '#fff',
          border: 'none',
          borderRadius: '8px',
          fontWeight: 600,
          cursor: isSubmitting ? 'not-allowed' : 'pointer',
        }}
      >
        {isSubmitting ? 'Submitting...' : 'Submit Instruction'}
      </button>
    </form>
  );
}
```

## Handling results

### Pending counterparty (`pending_match`) - share the trade reference

When the submission returns `pending_match`, the counterparty has not submitted yet. Show the trade reference prominently so the user can share it.

```tsx theme={null}
// pending-match-result.tsx
import { useState } from 'react';

interface PendingMatchResultProps {
  result: {
    id: string;
    tradeReference: string;
    status: string;
    role: string;
    createdAt: string;
  };
  onReset: () => void;
}

export function PendingMatchResult({ result, onReset }: PendingMatchResultProps) {
  const [copied, setCopied] = useState(false);

  const copyReference = async () => {
    await navigator.clipboard.writeText(result.tradeReference);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div style={{ maxWidth: '500px', textAlign: 'center', padding: '40px 24px' }}>
      <div style={{ fontSize: '48px', marginBottom: '16px' }}>
        <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" strokeWidth="2">
          <circle cx="12" cy="12" r="10" />
          <line x1="12" y1="8" x2="12" y2="12" />
          <line x1="12" y1="16" x2="12.01" y2="16" />
        </svg>
      </div>
      <h2 style={{ fontSize: '20px', fontWeight: 700, marginBottom: '8px' }}>Waiting for Counterparty</h2>
      <p style={{ color: '#6b7280', marginBottom: '24px' }}>
        Your {result.role} instruction has been submitted. Share the trade reference below with
        your counterparty so they can submit their counterpart instruction.
      </p>

      <div style={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        gap: '8px',
        padding: '16px',
        backgroundColor: '#f9fafb',
        borderRadius: '8px',
        border: '1px solid #e5e7eb',
        marginBottom: '24px',
      }}>
        <code style={{ fontSize: '20px', fontWeight: 700, letterSpacing: '0.05em' }}>
          {result.tradeReference}
        </code>
        <button
          onClick={copyReference}
          style={{
            padding: '6px 12px',
            backgroundColor: copied ? '#dcfce7' : '#e5e7eb',
            color: copied ? '#166534' : '#374151',
            border: 'none',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '13px',
          }}
        >
          {copied ? 'Copied' : 'Copy'}
        </button>
      </div>

      <p style={{ fontSize: '13px', color: '#9ca3af' }}>
        Instruction ID: {result.id}
        <br />
        Submitted: {new Date(result.createdAt).toLocaleString()}
      </p>

      <button
        onClick={onReset}
        style={{
          marginTop: '24px',
          padding: '10px 20px',
          backgroundColor: 'transparent',
          border: '1px solid #d1d5db',
          borderRadius: '6px',
          cursor: 'pointer',
        }}
      >
        Submit Another Instruction
      </button>
    </div>
  );
}
```

### Matched - navigate to settlement

When the submission returns `matched`, both sides are in. Navigate directly to the settlement detail page.

```tsx theme={null}
// matched-result.tsx
interface MatchedResultProps {
  result: {
    id: string;
    tradeReference: string;
    status: string;
    settlementId: string | null;
  };
}

export function MatchedResult({ result }: MatchedResultProps) {
  return (
    <div style={{ maxWidth: '500px', textAlign: 'center', padding: '40px 24px' }}>
      <div style={{ fontSize: '48px', marginBottom: '16px' }}>
        <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#16a34a" strokeWidth="2">
          <circle cx="12" cy="12" r="10" />
          <polyline points="16 10 11 15 8 12" />
        </svg>
      </div>
      <h2 style={{ fontSize: '20px', fontWeight: 700, marginBottom: '8px' }}>Settlement Created</h2>
      <p style={{ color: '#6b7280', marginBottom: '24px' }}>
        Both parties have submitted. The settlement is now being processed.
      </p>

      <div style={{ padding: '16px', backgroundColor: '#f0fdf4', borderRadius: '8px', border: '1px solid #bbf7d0', marginBottom: '24px' }}>
        <p style={{ fontSize: '13px', color: '#166534' }}>
          Trade Reference: <strong>{result.tradeReference}</strong>
        </p>
        <p style={{ fontSize: '13px', color: '#166534', marginTop: '4px' }}>
          Settlement ID: <strong>{result.settlementId}</strong>
        </p>
      </div>

      <a
        href={`/settlements/${result.settlementId}`}
        style={{
          display: 'inline-block',
          padding: '12px 24px',
          backgroundColor: '#2DD4A8',
          color: '#fff',
          borderRadius: '8px',
          textDecoration: 'none',
          fontWeight: 600,
        }}
      >
        View Settlement
      </a>
    </div>
  );
}
```

## Realistic example flow

Here is how a real trade looks end-to-end:

**1. Seller at Securitize submits both legs:**

```tsx theme={null}
await submit({
  role: 'seller',
  party: {
    external_reference: 'KSFI-II-4401',
    name: 'Securitize Fund I',
    wallet_address: '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18',
  },
  legs: [
    {
      leg_type: 'asset_delivery',
      instrument_id: '0x1234...abcd',   // OUSG token contract
      quantity: '12000000',
      direction: 'deliver',              // seller delivers the asset
      chain_id: 1,
    },
    {
      leg_type: 'payment',
      instrument_id: '0xA0b86...6EB48',  // USDC contract
      quantity: '12120000000000',
      direction: 'receive',              // seller receives payment
      chain_id: 1,
    },
  ],
  templateSlug: 'cross_platform_dvp',
  timeoutAt: '2026-04-02T15:00:00Z',
});
// Result: { status: 'pending_match', tradeReference: 'KS-abc12345', ... }
```

**2. Seller shares `KS-abc12345` with buyer via email or OMS.**

**3. Buyer at Ondo Capital submits with the trade reference:**

```tsx theme={null}
await submit({
  role: 'buyer',
  party: {
    external_reference: 'ONDO-CAP-8812',
    name: 'Ondo Capital',
    wallet_address: '0xfedcba9876543210fedcba9876543210fedcba98',
  },
  legs: [
    {
      leg_type: 'asset_delivery',
      instrument_id: '0x1234...abcd',   // same OUSG token contract
      quantity: '12000000',
      direction: 'receive',              // buyer receives the asset
      chain_id: 1,
    },
    {
      leg_type: 'payment',
      instrument_id: '0xA0b86...6EB48',  // same USDC contract
      quantity: '12120000000000',
      direction: 'deliver',              // buyer delivers payment
      chain_id: 1,
    },
  ],
  templateSlug: 'cross_platform_dvp',
  timeoutAt: '2026-04-02T15:00:00Z',
  tradeReference: 'KS-abc12345',
});
// Result: { status: 'matched', settlementId: '550e8400-e29b-41d4-a716-446655440000', ... }
```

**4. Settlement is created automatically.** The confirmation response carries the new `settlement_id`; the settlement starts at `INSTRUCTED` and the engine begins compliance checks. The first webhook both platforms receive is `settlement.state.awaiting_deposits` once compliance clears (or `settlement.state.compliance_checking` if a party is flagged).

## Error handling

`POST /v1/instructions` refuses a submission in this flow with one of the codes below, carried as the `code` field of the HTTP response body; the full list is in the [error codes reference](/guides/error-codes). The current `useSubmitInstruction` hook does not surface that field: its `error` holds the response's `detail` alone, so branch on `code` only where you read the HTTP response yourself.

| Error                           | What happened                                                                                                                                                                                                         | How to handle                                                                                                                       |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `DUPLICATE_RESOURCE`            | An instruction with this role already rests on the trade reference within your match scope (HTTP 409)                                                                                                                 | Cancel the resting instruction or wait for it to expire, then resubmit; a retry of the same submission reuses its `idempotency_key` |
| `UNSUPPORTED_CHAIN_COMBINATION` | The legs span a chain combination KeyStone cannot settle - today every leg must name the same chain (HTTP 422). The same refusal answers a submission whose legs and the resting counterpart's legs together span one | Put every leg on one `chain_id`                                                                                                     |
| `UNSUPPORTED_SETTLEMENT_CHAIN`  | The submitted legs name a chain this environment has no settlement contract on (HTTP 422)                                                                                                                             | Submit legs on a chain the environment settles on                                                                                   |
| `NON_INTEGRAL_QUANTITY`         | A leg `quantity` is not a whole number of base units (HTTP 422)                                                                                                                                                       | Send integer base units                                                                                                             |

A request that fails schema validation is refused with a `422` whose body carries `code: "VALIDATION_ERROR"` and a `docs_url` beside a `detail` array of `loc` / `msg` / `type` items - see [the error response format](/guides/error-handling#error-response-format). The hook surfaces that `detail` alone, as it does for every refusal above. Validate the form before submitting.

A submission that does not pair is not an error. When the trade reference has no eligible resting counterpart - the counterparty has not submitted yet, or their instruction is outside your match scope - the response is `201` with `status: "pending_match"`, the same response a first submission gets. A reference whose earlier pairing already completed can be used again: each pairing creates its own settlement.

## Next steps

<CardGroup cols={2}>
  <Card title="Settlement Dashboard" icon="table" href="/elements/guides/settlement-dashboard">
    Build a dashboard to monitor all your settlements.
  </Card>

  <Card title="Real-time Tracking" icon="signal" href="/elements/guides/realtime-tracking">
    Track settlement progress with live updates after confirmation.
  </Card>

  <Card title="useSubmitInstruction Reference" icon="code" href="/hooks/use-submit-instruction">
    Full API reference for the useSubmitInstruction hook.
  </Card>

  <Card title="Submitting Instructions Guide" icon="book" href="/guides/submitting-instructions">
    Server-side guide for instruction flow using the SDK.
  </Card>
</CardGroup>
