# Positions

Work with positions in the sandbox.

This is the simulated Earn API. Position creation and withdrawal change sandbox records; they do not sign a wallet transaction.


[Authentication](/build/authentication/) · [Errors](/build/errors/) · [Amounts and units](/build/units/)


## Open a position

`POST /positions`


Validates the wallet/asset/amount and routes to a vault. Settlement is synchronous: the position is active immediately.


### Parameters

| Name | Location | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `Idempotency-Key` | header | `string` | No | Client-generated key. Retries with the same key replay the original response. |


### Request body

Schema: [CreatePositionRequest](/api/sandbox-models/#createpositionrequest)


### Response · 201

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | `string` | Yes |   |
| `data` | [Position](/api/sandbox-models/#position) | Yes |   |
| `meta` | `object` | No |   |


For failed requests, inspect `error.code`, `error.message` and the request ID. [Error handling](/build/errors/).


## List positions

`GET /positions`


### Parameters

| Name | Location | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `wallet` | query | `string` | No | Filter by wallet address |

| `status` | query | `"active"`, `"closed"` | No | Filter by status |

| `limit` | query | `integer` | No | Page size (1-500, default 100) |

| `cursor` | query | `string` | No | Opaque cursor returned as meta.next_cursor |

### Response · 200

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | `"list"` | Yes |   |
| `data` | [Position](/api/sandbox-models/#position)[] | Yes |   |
| `meta` | [ListMeta](/api/sandbox-models/#listmeta) | Yes |   |


For failed requests, inspect `error.code`, `error.message` and the request ID. [Error handling](/build/errors/).


## Get a position

`GET /positions/{id}`


### Parameters

| Name | Location | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `id` | path | `string` | Yes | Position id |

### Response · 200

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | `string` | Yes |   |
| `data` | [Position](/api/sandbox-models/#position) | Yes |   |
| `meta` | `object` | No |   |


For failed requests, inspect `error.code`, `error.message` and the request ID. [Error handling](/build/errors/).


## Withdraw from a position

`POST /positions/{id}/withdraw`


Partial or full withdrawal. Partial withdrawals preserve accrued yield exactly (the remaining value keeps compounding).


### Parameters

| Name | Location | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `id` | path | `string` | Yes | Position id |


### Request body

Schema: [WithdrawRequest](/api/sandbox-models/#withdrawrequest)


### Response · 200

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | `string` | Yes |   |
| `data` | [Position](/api/sandbox-models/#position) | Yes |   |
| `meta` | `object` | No |   |


For failed requests, inspect `error.code`, `error.message` and the request ID. [Error handling](/build/errors/).


## Position event timeline

`GET /positions/{id}/history`


### Parameters

| Name | Location | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `id` | path | `string` | Yes | Position id |

| `limit` | query | `integer` | No | Page size (1-500, default 100) |

| `cursor` | query | `string` | No | Opaque cursor returned as meta.next_cursor |

### Response · 200

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | `"list"` | Yes |   |
| `data` | [PositionEvent](/api/sandbox-models/#positionevent)[] | Yes |   |
| `meta` | [ListMeta](/api/sandbox-models/#listmeta) | Yes |   |


For failed requests, inspect `error.code`, `error.message` and the request ID. [Error handling](/build/errors/).

