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

# POST /recover

> Get back what a failed Hinkal swap left at Hinkal, to the wallet that paid.

A [Hinkal](/supported.md#hinkal) private swap whose payout fails is **not refunded on its own**. The
funds wait at Hinkal, on the chain the swap started from: in a temporary wallet when the bridge
failed, or in Hinkal's pool when the payout never ran. This endpoint gets them back. Only the wallet
that paid for the swap can sign the recovery, and the funds only ever go back to that wallet, never
to an address from the request.

Use it when [`/status`](/api-reference/status) reports the swap `failed` with `venueStatus` ending
`(needs recovery)`, or when its deposit has confirmed but `/status` still answers `pending` 25
minutes after the order was placed (a payout that never ran). RAVN can recover a swap while it holds its record, 7 days after the order was
placed.

> [!WARNING]
>
> **A recovery is public.** It is an ordinary on-chain payment back to the paying wallet, which can
> link that wallet to the one it was paying: the link the private swap was hiding. Tell your user
> before they sign.

## Request

**statusRef** `string` *required*

The `statusRef` of the Hinkal swap's `TRANSACTION` from [`/execute`](/api-reference/execute) (Hinkal's order id). Alone, it always answers `sign` step 1, the same answer for any order id, so it tells no one but the payer anything about the swap.

**authorization** `string`

After a `sign` answer: its `authorization` (`hk_` and 32 hex characters), with the paying wallet's `signature` over its `sign`. After `done`: its `authorization` alone, with no signature, to look for more.

**signature** `string`

With a `sign` answer's `authorization`: the paying wallet's signature over its `sign`, `0x` hex. Never with the `authorization` from `done`.

No API key required (see [Authentication](/authentication.md)): up to 30 calls an hour per IP, or 300
per key.

```bash theme={null}
curl -s -X POST https://app.ravn.exchange/api/v1/recover \
  -H 'content-type: application/json' \
  -d '{ "statusRef": "<the Hinkal TRANSACTION statusRef>" }'
```

## The flow

**1. Ask with the statusRef alone**

`{ statusRef }` answers `sign` step 1: a `personal_sign` message that opens a read-only Hinkal
session for the paying wallet. Check it as on [`/execute`](/execution-types#authorize-steps-hinkal):
exactly `Authorize Hinkal session`, `Session ID: …`, `Public Key: …`, and never one that adds
`This signature can also be used to submit transactions.`

**2. Prove the wallet paid**

`{ statusRef, authorization, signature }` with that signature. Hinkal opens the session only for
the swap's paying wallet, so a signature from any other wallet is refused (`400`). It answers
`sign` step 2, a recovery to sign with `eth_signTypedData_v4`, or `nothing`.

**3. Sign the recovery**

Check the typed data before your user signs it: domain `{ name: "Hinkal Enclave", chainId }` on
the swap's source chain, `primaryType` `RecoverStuckBridgeBalance` or `WithdrawStuckUtxos`,
`message.chainId` equal to the domain's, one `tokenAddress`, and `recipient` the paying wallet.
Send `{ statusRef, authorization, signature }` again: the funds are sent, and the answer is
`done` with what was recovered.

**4. Look again**

A swap can leave more than one token behind. Send `{ statusRef, authorization }` with the
`authorization` from `done` and **no** signature: it looks again under the wallet already proven,
with no new session, and answers step 2 again or `nothing`. Repeat until `nothing`.

Each `authorization` is single-use, tied to this `statusRef`, and lasts 10 minutes (a `sign`
answer's `expiresAt`). Sign and call again before then.

## Response

```json sign theme={null}
{ "data": {
    "status": "sign",
    "authorization": "hk_…",
    "sign": { "method": "personal_sign", "message": "Authorize Hinkal session\nSession ID: …\nPublic Key: …" },
    "expiresAt": "2026-10-22T12:09:00.000Z",
    "step": 1 } }
```

```json done theme={null}
{ "data": {
    "status": "done",
    "recovered": [{ "chainId": 8453, "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "amount": "49650000" }],
    "authorization": "hk_…" } }
```

```json nothing theme={null}
{ "data": { "status": "nothing" } }
```

**status** `string`

`sign`: a step to sign (`step` 1, the session, with `sign.message`; `step` 2, a recovery, with `sign.typedData`). `done`: a recovery was sent. `nothing`: nothing to recover.

**recovered** `object[]`

With `done`: `chainId`, `tokenAddress` and `amount` (in the token's smallest unit) of what was recovered, before Hinkal's recovery fee, which it deducts: the wallet receives slightly less.

`nothing` means nothing is stuck: the payout is still on its way, it was delivered, it was already
recovered, or RAVN holds no such swap (its record lasts 7 days). A bridge refund shows at Hinkal
about 3 minutes after the payout fails, so a call made sooner can answer `nothing`: try again a few
minutes later. A deposit whose payout never ran is offered once the order has failed, or 25 minutes
after it was placed with no payout scheduled, never while a payout may still spend it.

## Errors

| Code | When |
| - | - |
| `400 INVALID_REQUEST` | A `statusRef` that isn't a Hinkal order id; a signature from another wallet than the one that paid ("sign with the wallet that paid"); an expired or already used `authorization` (start again from step 1) |
| `429 RATE_LIMITED` | Too many recovery calls: the message says how many minutes to wait |
| `502 BUILD_FAILED` | Hinkal can't be reached, or refused the recovery: try again shortly |
| `500 INTERNAL` | The recovery couldn't be checked right now: try again shortly |

## With the SDK

[`@ravnexchange/sdk`](/sdks/core#recoverstuckfunds) 0.4.1 or later runs the whole flow, checking
each step before your user signs it:

```ts theme={null}
import { recoverStuckFunds } from "@ravnexchange/sdk";

const { recovered } = await recoverStuckFunds(
  client,
  { statusRef: execution.statusRef, userAddress }, // userAddress: the paying wallet, from your own connection
  {
    signMessage: (message) => wallet.signMessage({ message }),
    signTypedData: (typedData) => wallet.signTypedData(typedData),
    switchChain: (chainId) => wallet.switchChain({ id: chainId }), // optional
  }
);
```

In Python, [`ravnexchange`](/sdks/python#recover_stuck_funds) 0.1.7 or later does the same with
`recover_stuck_funds(client, status_ref, user_address, sign_message, sign_typed_data)`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.