# X-Trader transfer-snipe API

URL: https://docs.blizz.id/xtrader/transfer-snipe/
Updated: 2026-10-16

A transfer-snipe task is a user-scoped X-Trader task that watches one wallet (the spec calls it watch wallet A, one per task) and carries a buy and sell strategy whose `config` schema is identical to the sniper config. It is managed through seven operations under `/transfer-snipe/configs` with the `transfersnipe:read`, `transfersnipe:write` and `transfersnipe:delete` scopes. Like sniper and copy-trade tasks, it is always created paused and runs only after `POST /transfer-snipe/configs/{id}/resume`.

Primary source: [X-Trader API reference](https://x-trader.ai/api/docs). This page is an independent explanation of X-Trader Public API v1.0.0; it is not affiliated with X-Trader.

## What the spec says about transfer-snipe

- The tag description defines a transfer-snipe task as having one watch wallet A per task, with creation paused by design.
- The only wallet field in the request body is `target_wallet`, so that field holds wallet A. It is a single Solana address and is immutable after creation.
- `TransferSnipeCreate` states that its config shape matches sniper; the `config` schemas of `SniperCreate` and `TransferSnipeCreate` are the same.
- Transfer-snipe tasks share the label set with sniper and copy-trade (`LabelResource.transfer_snipe_count`) and count toward the same configuration quota reported by `GET /me`.

The spec does not document which on-chain transfer events from wallet A trigger a buy, nor how the purchased token is chosen. It also defines no transfer-snipe templates.

## Operations

| Method and path | Purpose | Scope | Required headers | Success |
|---|---|---|---|---|
| `GET /transfer-snipe/configs` | List owned tasks | `transfersnipe:read` | `Authorization` | `200` list envelope |
| `POST /transfer-snipe/configs` | Create a paused task | `transfersnipe:write` | `Authorization`, `Idempotency-Key` | `201` resource envelope |
| `GET /transfer-snipe/configs/{id}` | Read one task | `transfersnipe:read` | `Authorization` | `200` resource envelope |
| `PATCH /transfer-snipe/configs/{id}` | Update mutable fields | `transfersnipe:write` | `Authorization`, `If-Match` | `200` resource envelope |
| `DELETE /transfer-snipe/configs/{id}` | Delete a paused task | `transfersnipe:delete` | `Authorization`, `If-Match` | `204` no body |
| `POST /transfer-snipe/configs/{id}/pause` | Stop execution | `transfersnipe:write` | `Authorization`, `If-Match` | `200` resource envelope |
| `POST /transfer-snipe/configs/{id}/resume` | Revalidate and start | `transfersnipe:write` | `Authorization`, `If-Match` | `200` resource envelope |

Base URL `https://x-trader.ai/api/v1`. Error statuses listed for every operation: `400`, `401`, `403`, `404`, `409`, `429`, `500`, `503`. No bulk delete.

## Headers

| Header | Where | Rule |
|---|---|---|
| `Authorization` | All | `Bearer xt_live_<id>.<secret>` |
| `Idempotency-Key` | Create | 8-128 chars, `A-Z a-z 0-9 . _ : -`, retained 24 hours; reuse with another body gives `idempotency_conflict` |
| `If-Match` | PATCH, DELETE, pause, resume | Latest `ETag`, `"v<number>"`; stale gives `version_conflict` |
| `ETag` (response) | 201/200 single-resource | Send as `If-Match` next time |
| `X-Request-Id` (response) | Single-resource and delete | Support correlation |

Shared write rules: [/xtrader/write-rules/](/xtrader/write-rules/).

## List: GET /transfer-snipe/configs

| Query parameter | Type | Rule |
|---|---|---|
| `limit` | integer | 1-100, default 20 |
| `cursor` | string | Max 2048; opaque `next_cursor`, bound to token and filters |
| `status` | string | `active` or `paused` |
| `q` | string | Max 64; searches `remark` and `target_wallet` |
| `label_id` | string | Owned label UUID or `unlabeled`; foreign or missing UUID returns `404` |

Ordering is by `updated_at` and `id`. Response: `data.items`, `data.next_cursor`, `meta.request_id`. See [/xtrader/pagination/](/xtrader/pagination/).

## TransferSnipeCreate fields

Top level (strict; `user_id`, `version`, `template` and unknown fields are rejected):

| Field | Type | Required | Default | Constraint |
|---|---|---|---|---|
| `remark` | string | yes | `""` | Max 64 characters, trimmed |
| `target_wallet` | string | yes | | Watch wallet A. Solana address, 32-44 characters, immutable |
| `risk_label_id` | string (UUID) or null | no | | Label owned by the account; `null` unassigns; foreign ID returns `404` |
| `config` | object | yes | | Complete strategy, schema below |

`config` (same schema as the sniper config; full reference at [/xtrader/config-sniper/](/xtrader/config-sniper/)):

| Field | Type | Required | Default | Constraint and meaning |
|---|---|---|---|---|
| `buy_amount` | decimal string | yes | | SOL per buy, greater than 0 and at most 1,000,000 |
| `jito_mode` | object | yes | | Jito route fees: `tip_amount` (0-1,000 SOL), `priority_fee` (0-1,000 SOL), `slippage` (0-100 %) required; optional `bundle_amount`, `sol_slippage_mode` |
| `priority_mode` | object | yes | | Leader/direct route fees, same keys and ranges as `jito_mode` |
| `bundle_amount` | decimal string | no | | Legacy top-level fallback; prefer the copy inside `jito_mode` / `priority_mode` |
| `selected_dexes` | array | yes | | 1-5 of `pump-fun`, `launchlab`, `believe`, `bags`, `meteora-dynamic-bc` |
| `dex_source` | string | yes | `global` | `global` uses the enabled global DEX list (falls back to `selected_dexes`); `config` always uses `selected_dexes` |
| `dev_min_amount` | decimal string | no | | Lower bound of the creator buy filter; `""` disables it |
| `dev_max_amount` | decimal string | no | | Upper bound; `""` disables it; must be at least `dev_min_amount` when both are set |
| `buy_once` | boolean | yes | `false` | Deactivate the task after its first triggered trade |
| `sol_slippage_mode` | boolean | yes | `false` | Legacy shared SOL-slippage flag; per-mode flags take precedence |
| `snipe_external` | boolean | yes | `false` | Skip the bonding curve and use supported external-pool execution only |
| `auto_sell` | object | yes | | TP, SL, trailing TP, follow-sell; see [/xtrader/auto-sell/](/xtrader/auto-sell/) |
| `auto_strategy` | object | yes | | Migration, timed sell, protections, sell-fee override; see [/xtrader/auto-strategy/](/xtrader/auto-strategy/) |
| `timed_strategy` | object | yes | | Up to 20 delayed sell orders; see [/xtrader/timed-strategy/](/xtrader/timed-strategy/) |

All amounts, fees, percentages and durations are decimal strings (at most 12 fractional digits, max 32 characters), never JSON numbers. Every nested object is strict.

`TransferSnipeResource` adds the server fields `id`, `is_active`, `version`, `created_at` and `updated_at`, and returns `remark`, `target_wallet`, `risk_label_id` and `config`.

## Create

```bash
curl --request POST 'https://x-trader.ai/api/v1/transfer-snipe/configs' \
  --header "Authorization: Bearer $X_TRADER_API_TOKEN" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: create-transfer-20261016-001' \
  --data '{
    "remark": "Watch wallet A",
    "target_wallet": "11111111111111111111111111111111",
    "config": {
      "buy_amount": "0.1",
      "jito_mode": {"tip_amount": "0.01", "priority_fee": "0.0005", "slippage": "15", "bundle_amount": ""},
      "priority_mode": {"tip_amount": "0.001", "priority_fee": "0.005", "slippage": "15", "bundle_amount": ""},
      "selected_dexes": ["pump-fun"],
      "dex_source": "global",
      "dev_min_amount": "",
      "dev_max_amount": "",
      "buy_once": false,
      "sol_slippage_mode": false,
      "snipe_external": false,
      "auto_sell": {
        "enabled": false, "tab": "tpsl", "expiry_time": "10",
        "tp_orders": [{"trigger": "90", "sell": "100"}], "sl_orders": [],
        "follow_sell": false, "proportional_sell": false,
        "trailing_take_profit": {"enabled": false, "tiers": []}
      },
      "auto_strategy": {"migration_clear": false, "timed_sell": false, "sell_delay": "30"},
      "timed_strategy": {"enabled": false, "orders": []}
    }
  }'
```

Response `201 Created` (headers `ETag: "v1"`, `X-Request-Id`; IDs and timestamps illustrative):

```json
{
  "data": {
    "id": "c4e7a2b0-91d3-4f5e-a6b8-0d2c9e1f3a75",
    "remark": "Watch wallet A",
    "target_wallet": "11111111111111111111111111111111",
    "is_active": false,
    "risk_label_id": null,
    "version": 1,
    "created_at": "2026-10-16T09:00:00Z",
    "updated_at": "2026-10-16T09:00:00Z",
    "config": { "buy_amount": "0.1", "...": "stored config as sent" }
  },
  "meta": {
    "request_id": "req_0123456789abcdef",
    "persistence": "committed",
    "propagation": "eventual"
  }
}
```

## Get, patch, delete

- `GET /transfer-snipe/configs/{id}`: missing and foreign IDs both return `404 not_found`. The stored snapshot is returned as is, possibly with legacy fields.
- `PATCH /transfer-snipe/configs/{id}` (`TransferSnipePatch`): at least one of `remark`, `risk_label_id`, `config`. While active, only `remark` and `risk_label_id` may change; pause before changing `config`. A `config` in the patch replaces the whole stored config and must pass the full schema. `target_wallet` cannot be patched. The operation text also mentions `linked_wallets`, but `TransferSnipePatch` has no such field and unknown fields are rejected.
- `DELETE /transfer-snipe/configs/{id}`: only paused tasks; `204` on success.

## Pause and resume

Pause updates the stored state immediately; execution nodes pick it up eventually. Resume revalidates the complete stored config before activation and will not activate invalid historical data.

```bash
curl --request POST 'https://x-trader.ai/api/v1/transfer-snipe/configs/c4e7a2b0-91d3-4f5e-a6b8-0d2c9e1f3a75/resume' \
  --header "Authorization: Bearer $X_TRADER_API_TOKEN" \
  --header 'If-Match: "v1"'
```

Response `200 OK` with the new `ETag` header (values illustrative):

```json
{
  "data": {
    "id": "c4e7a2b0-91d3-4f5e-a6b8-0d2c9e1f3a75",
    "remark": "Watch wallet A",
    "target_wallet": "11111111111111111111111111111111",
    "is_active": true,
    "risk_label_id": null,
    "version": 2,
    "created_at": "2026-10-16T09:00:00Z",
    "updated_at": "2026-10-16T09:01:10Z",
    "config": { "buy_amount": "0.1", "...": "unchanged stored config" }
  },
  "meta": { "request_id": "req_0123456789abcdef", "persistence": "committed", "propagation": "eventual" }
}
```

### Python: pause, change buy amount, resume

```python
import os
import requests

BASE = "https://x-trader.ai/api/v1"
H = {"Authorization": f"Bearer {os.environ['X_TRADER_API_TOKEN']}"}
tid = "c4e7a2b0-91d3-4f5e-a6b8-0d2c9e1f3a75"

r = requests.get(f"{BASE}/transfer-snipe/configs/{tid}", headers=H)
r.raise_for_status()
task, etag = r.json()["data"], r.headers["ETag"]

if task["is_active"]:
    r = requests.post(f"{BASE}/transfer-snipe/configs/{tid}/pause", headers={**H, "If-Match": etag})
    r.raise_for_status()
    etag = r.headers["ETag"]

config = task["config"]          # full object: PATCH replaces config completely
config["buy_amount"] = "0.2"     # decimal string, not a number

r = requests.patch(f"{BASE}/transfer-snipe/configs/{tid}",
                   json={"config": config}, headers={**H, "If-Match": etag})
r.raise_for_status()

r = requests.post(f"{BASE}/transfer-snipe/configs/{tid}/resume",
                  headers={**H, "If-Match": r.headers["ETag"]})
r.raise_for_status()
```

If the stored snapshot contains legacy web-UI fields, the PATCH or resume can fail with `validation_error`; remove the fields listed in `error.fields` and send a config that matches the current schema.

## Lifecycle

1. Create (paused) with an `Idempotency-Key`.
2. Resume with the `ETag` from the create response.
3. To change the strategy: pause, PATCH the full `config`, resume.
4. `remark` and `risk_label_id` can change without pausing (still with `If-Match`).
5. To remove: pause, then delete.

## Errors

Error body: `{ "error": { "code", "message", "fields"? }, "request_id" }`.

| Code | Typical cause on /transfer-snipe/configs |
|---|---|
| `validation_error` | Unknown field, number instead of decimal string, empty PATCH, failed resume revalidation |
| `insufficient_scope` | Token lacks the needed `transfersnipe:*` scope |
| `not_found` | Unknown or foreign task ID, foreign label |
| `config_must_be_paused` | `config` change or delete while active |
| `version_conflict` | Stale `If-Match` |
| `idempotency_required` / `idempotency_conflict` | Missing key, or key reused with another body |
| `quota_exceeded` | Shared configuration quota full |
| `rate_limit_exceeded`, `service_unavailable` | `429` / `503` with `Retry-After` |

The spec does not say whether `target_already_exists` applies to transfer-snipe. Full list: [/xtrader/errors/](/xtrader/errors/).

## FAQ

### What is a transfer-snipe task in the X-Trader API?

It is a user-scoped task with exactly one watch wallet (wallet A), stored in `target_wallet`, plus a strategy `config` whose shape matches the sniper config. Like other tasks it is created paused.

### Which scopes does transfer-snipe use?

`transfersnipe:read` for list and get, `transfersnipe:write` for create, patch, pause and resume, and `transfersnipe:delete` for delete.

### Can one transfer-snipe task watch several wallets?

No. The spec defines one watch wallet A per task, and `target_wallet` is a single immutable address. Create one task per wallet.

### Is the transfer-snipe config different from the sniper config?

No. The `config` object in `TransferSnipeCreate` and `TransferSnipePatch` has the same schema as the sniper config, including `buy_amount`, `jito_mode`, `priority_mode` and the sell objects.

### Are there transfer-snipe templates?

The spec defines templates only for sniper and copy-trade. There are no `/transfer-snipe/templates` endpoints.
