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

# Optimize Strategy

> Given customer preferences, returns a yield-maximizing allocation that plugs directly into wallet creation.

`POST /v2/wallets/strategy/optimize` computes the highest-APY allocation for one stablecoin strategy lane and returns a token-keyed `allocations` object you can pass straight into `POST /v2/wallets` or `PATCH /v2/wallets/{id}/strategy`.

## Request

All request body fields are optional. `token` defaults to `usdc`; submit an empty body `{}` to get the unconstrained highest-APY allocation across available USDC sources. Set `token` to `usdt` to optimize the USDT lane. Sources for other deposit tokens are excluded before optimization.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "$BASE_URL/v2/wallets/strategy/optimize" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "usdc",
    "minBlendedApyBps": 400,
    "concentration": { "maxPerProtocolPct": 60 },
    "liquidity": { "maxSettlementHours": 24 }
  }'
```

| Field                             | Type             | Description                                                                                                                                                                                                                                         |
| --------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `token`                           | `usdc` or `usdt` | Stablecoin strategy lane to optimize. Defaults to `usdc`. Only sources whose `depositToken` matches are eligible.                                                                                                                                   |
| `minBlendedApyBps`                | integer, 0–10000 | Preferred minimum blended APY in basis points. If unreachable, the best-achievable allocation is still returned with `unmetConstraints: ["minBlendedApyBps"]`.                                                                                      |
| `liquidity.maxSettlementHours`    | integer, ≥0      | Exclude any source whose maximum processing time exceeds this number of hours. `0` means instant-liquidity only.                                                                                                                                    |
| `concentration.maxPerSourcePct`   | integer, 1–100   | Cap on any single source's allocation percentage.                                                                                                                                                                                                   |
| `concentration.maxPerProtocolPct` | integer, 1–100   | Cap on the combined allocation across all sources sharing the same protocol slug. For example, `morpho-august-usdc-v2`, `morpho-gauntlet-usdc`, `morpho-steakhouse-usdc`, and `morpho-smokehouse-usdc` all count against the `morpho` protocol cap. |
| `allowedChains`                   | array of strings | Restrict eligible sources to the listed chain slugs (e.g. `["ethereum", "base"]`). Must be a non-empty array if provided. Omit to allow all chains.                                                                                                 |
| `excludedSources`                 | array of strings | Yield source IDs to exclude (e.g. `["syrup-usdc"]`).                                                                                                                                                                                                |
| `excludedProtocols`               | array of strings | Protocol slugs to exclude entirely (e.g. `["morpho"]`). Case-insensitive: `"MORPHO"`, `"Morpho"`, and `"morpho"` are equivalent.                                                                                                                    |
| `maxSources`                      | positive integer | Cap on the number of sources in the resulting allocation.                                                                                                                                                                                           |

## Response (200)

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "allocations": {
    "usdc": [
      { "yieldSourceId": "morpho-august-usdc-v2", "pct": 60 },
      { "yieldSourceId": "morpho-gauntlet-usdc", "pct": 40 }
    ]
  },
  "summary": {
    "blendedApyBps": 498,
    "sourceCount": 2,
    "protocolCount": 1,
    "liquidityProfile": {
      "PT0H": 100
    }
  },
  "sources": [
    {
      "yieldSourceId": "morpho-august-usdc-v2",
      "pct": 60,
      "apyBps": 563,
      "protocol": "morpho",
      "chain": "ethereum",
      "maxProcessingTime": "PT0H",
      "expectedProcessingTime": "PT0H"
    },
    {
      "yieldSourceId": "morpho-gauntlet-usdc",
      "pct": 40,
      "apyBps": 472,
      "protocol": "morpho",
      "chain": "ethereum",
      "maxProcessingTime": "PT0H",
      "expectedProcessingTime": "PT0H"
    }
  ]
}
```

### `allocations.<token>[]`

| Field           | Description                                                                                    |
| --------------- | ---------------------------------------------------------------------------------------------- |
| `yieldSourceId` | Yield source identifier. The source's `depositToken` matches the response group containing it. |
| `pct`           | Allocated percentage (0-100, supporting 0.1% increments). All entries sum to exactly 100.      |

### `summary`

| Field              | Description                                                                                                                                                                                                                                                                                 |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `blendedApyBps`    | Weighted-average APY of the allocation in basis points (conservative floor, never overstated).                                                                                                                                                                                              |
| `sourceCount`      | Number of sources used in the allocation.                                                                                                                                                                                                                                                   |
| `protocolCount`    | Number of distinct protocols used.                                                                                                                                                                                                                                                          |
| `liquidityProfile` | Object mapping ISO 8601 duration → combined percentage. Each key is a `maxProcessingTime` bucket; the value is the total percentage allocated to sources with that liquidity tier. Example: `{ "PT0H": 70, "PT24H": 30 }` means 70% is instantly liquid and 30% has up to a 24-hour unwind. |

### `sources[]`

Per-source detail mirroring the allocation. Useful for displaying source-level metadata before committing to a strategy.

| Field                    | Description                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------------- |
| `yieldSourceId`          | Same as in `allocations.<token>[]`.                                                         |
| `pct`                    | Allocated percentage for this source.                                                       |
| `apyBps`                 | Current APY for this source in basis points.                                                |
| `protocol`               | Protocol slug (e.g. `morpho`, `maple`, `kamino`).                                           |
| `chain`                  | Chain slug (e.g. `ethereum`, `base`).                                                       |
| `maxProcessingTime`      | ISO 8601 duration representing the worst-case withdrawal completion time (SLA upper bound). |
| `expectedProcessingTime` | ISO 8601 duration representing the typical withdrawal completion time.                      |

### `unmetConstraints` (optional)

If the best-achievable allocation fails to satisfy one or more of your non-hard constraints, `unmetConstraints` appears with an array of the failed constraint names. Today the only value that can appear is `"minBlendedApyBps"` — when your requested APY floor is higher than what's achievable. The caller can decide whether to accept the returned allocation or loosen the floor.

When all constraints are satisfied, `unmetConstraints` is absent from the response entirely.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "allocations": {
    "usdc": [
      { "yieldSourceId": "morpho-august-usdc-v2", "pct": 100 }
    ]
  },
  "summary": {
    "blendedApyBps": 563,
    "sourceCount": 1,
    "protocolCount": 1,
    "liquidityProfile": { "PT0H": 100 }
  },
  "sources": [
    {
      "yieldSourceId": "morpho-august-usdc-v2",
      "pct": 100,
      "apyBps": 563,
      "protocol": "morpho",
      "chain": "ethereum",
      "maxProcessingTime": "PT0H",
      "expectedProcessingTime": "PT30M"
    }
  ],
  "unmetConstraints": ["minBlendedApyBps"]
}
```

## Infeasibility (422)

When the optimizer cannot produce a valid allocation, it returns a 422 with a machine-readable `reason` code.

### `NO_ELIGIBLE_SOURCES`

The eligibility filters (chain, liquidity, excluded sources/protocols) produced an empty candidate set.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": "INFEASIBLE",
  "reason": "NO_ELIGIBLE_SOURCES",
  "detail": "No yield sources satisfy the supplied constraints."
}
```

### `CAPS_PREVENT_FULL_ALLOCATION`

Eligible sources cannot be combined to sum to 100% under the supplied concentration or `maxSources` caps.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "error": "INFEASIBLE",
  "reason": "CAPS_PREVENT_FULL_ALLOCATION",
  "detail": "Supplied concentration / maxSources caps cannot reach 100% allocation with eligible sources."
}
```

## Validation errors (400)

Malformed request body fields return 400 with a human-readable error message:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{ "error": "minBlendedApyBps must be an integer between 0 and 10000" }
```

Common validation failures:

| Field                                          | Example error                                                        |
| ---------------------------------------------- | -------------------------------------------------------------------- |
| `minBlendedApyBps` out of range or non-integer | `minBlendedApyBps must be an integer between 0 and 10000`            |
| `liquidity.maxSettlementHours` negative        | `liquidity.maxSettlementHours must be a non-negative integer`        |
| `concentration.maxPerSourcePct` out of range   | `concentration.maxPerSourcePct must be an integer between 1 and 100` |
| `allowedChains` is an empty array              | `allowedChains must be a non-empty array if provided`                |
| `maxSources` non-positive                      | `maxSources must be a positive integer`                              |

## Worked example — chaining into wallet creation

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
// 1. Ask for an allocation
const optimizeRes = await fetch(`${API_BASE}/v2/wallets/strategy/optimize`, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({
        token: 'usdc',
        minBlendedApyBps: 400,
        concentration: { maxPerProtocolPct: 60 }
    })
});
const { allocations } = await optimizeRes.json();

// 2. Pass straight into wallet creation
await fetch(`${API_BASE}/v2/wallets`, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({
        requestId: crypto.randomUUID(),
        label: 'Customer portfolio',
        strategy: { allocations }
    })
});
```

<Note>
  The token-keyed `allocations` object returned by this endpoint is designed to be passed directly into `POST /v2/wallets` or `PATCH /v2/wallets/{id}/strategy` without transformation.
</Note>

<Note>
  APY rates change continuously. The allocation returned reflects rates at request time. For wallets that will be created minutes or hours later, consider fetching a fresh recommendation closer to creation time.
</Note>
