Skip to content

LP WebSocket automation

Recommended for automated LPs: use the WebSocket feed for low-latency order discovery, and use REST for claiming, fiat-sent, collateral, and anything that must trigger webhooks or collateral logic reliably.

  • Stream: wss://api.rampwire.app/ws/orders
  • Auth: lp_key query parameter required (verified against the database before the socket upgrades).
  • Connection limit: 5 concurrent WebSocket connections per lp_key (additional attempts receive HTTP 429 with JSON error).

Alternate auth (no key in URL): connect without lp_key, receive { "type":"auth_required", ... }, then send { "type":"auth", "lp_key":"lp_…" } — the server validates and sends init if allowed.


Connect

text
wss://api.rampwire.app/ws/orders?lp_key=lp_a1b2c3d4&currencies=KES,NGN
  • currencies: comma-separated filter. If you include it, broadcasts that carry a currency list may only be delivered when there is overlap with your subscription (empty filter = no currency-based filtering on receive).
  • Use TLS (wss) in production.

Quick test (Node — ws package)

bash
npm install ws
javascript
import WebSocket from "ws";

const LP_KEY = process.env.LP_KEY || "lp_a1b2c3d4";
const ws = new WebSocket(
  `wss://api.rampwire.app/ws/orders?lp_key=${encodeURIComponent(LP_KEY)}&currencies=KES,NGN`,
);

ws.on("message", (data) => console.log(JSON.parse(data.toString())));
ws.on("open", () => setInterval(() => ws.send(JSON.stringify({ type: "ping" })), 30_000));

Messages you receive (server → client)

init

Sent immediately after a successful connection when the LP is known.

json
{
  "type": "init",
  "orders": [],
  "lp_id": 12,
  "currencies": ["KES", "NGN"]
}
  • orders: up to 20 most recent rows with status = "open" (newest first).
  • Shape aligns with the order preview object (below).

new_order

Broadcast when a matching sell order is created.

json
{
  "type": "new_order",
  "order": {
    "id": 10042,
    "amount_fiat": 5000,
    "currency": "KES",
    "amount_usd": 38.79,
    "crypto_symbol": "usdt",
    "crypto_chain": "trx",
    "status": "open",
    "created_at": "2026-05-03T12:34:56.000Z"
  }
}

order_taken

Another LP claimed the order via the WebSocket claim message; your UI or bot should drop the order from “available” if you were racing.

json
{ "type": "order_taken", "order_id": 10042 }

Note: Competitors who claim with HTTP POST /api/orders/{id}/claim may not trigger this broadcast. Always treat claim success as authoritative only after your own HTTP response (or your own WS claimed message if you use that path).

pong (heartbeat)

json
{ "type": "pong", "ts": 1746275096123 }

auth_required (deferred auth only)

json
{
  "type": "auth_required",
  "message": "Send {\"type\":\"auth\",\"lp_key\":\"lp_...\"} to continue"
}

error

json
{ "type": "error", "message": "Invalid lp_key" }

claimed (outbound claim acknowledgement)

json
{ "type": "claimed", "order_id": 10042 }

Messages you send (client → server)

ping — keepalive

Send about every 30 seconds.

json
{ "type": "ping" }

claim

json
{ "type": "claim", "order_id": 10042 }

Updates the order to claimed if still open. The server echoes claimed and may broadcast order_taken to other subscribers.

Production tip: Prefer POST https://api.rampwire.app/api/orders/{id}/claim with Authorization: Bearer {lp_key} so collateral locks and lp_receive_address are applied the same way as the web app. Use the socket for discovery, HTTP for the economic handshake.

fiat_sent

json
{ "type": "fiat_sent", "order_id": 10042 }

Moves claimedfiat_sent when you own the order. Webhook side effects may differ from the HTTP route; prefer POST /api/orders/{id}/fiat-sent for integrators relying on callbacks.

auth (deferred auth)

json
{ "type": "auth", "lp_key": "lp_a1b2c3d4" }

Order object shape

Fields included in init / new_order:

FieldTypeNotes
idnumberPrimary key / order_id elsewhere
amount_fiatnumberLocal fiat amount
currencystringe.g. KES, NGN
amount_usdnumberUSD notional used internally
crypto_symbolstringe.g. usdt, xrp
crypto_chainstringe.g. trx, xrpl
statusstringe.g. open
created_atstringISO timestamp

Full automation example — Node.js

Scenario: auto-claim when amount_fiat is below your max and currency is allowed; send M-Pesa manually or via your own provider; mark fiat via HTTP; poll order until complete.

javascript
import WebSocket from "ws";

const API = "https://api.rampwire.app";
const LP_KEY = process.env.LP_KEY;
const MAX_KES = 20_000;
const ALLOWED = new Set(["KES"]);

async function claimHttp(orderId) {
  const res = await fetch(`${API}/api/orders/${orderId}/claim`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${LP_KEY}`,
    },
    body: "{}",
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

async function fiatSentHttp(orderId) {
  const res = await fetch(`${API}/api/orders/${orderId}/fiat-sent`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${LP_KEY}`,
    },
    body: "{}",
  });
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

const ws = new WebSocket(
  `wss://api.rampwire.app/ws/orders?lp_key=${encodeURIComponent(LP_KEY)}&currencies=KES`,
);

ws.on("message", async (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.type !== "new_order") return;
  const o = msg.order;
  if (!ALLOWED.has(o.currency)) return;
  if (o.amount_fiat >= MAX_KES) return;
  try {
    const claimed = await claimHttp(o.id);
    console.log("claimed", claimed);
    // Integrate M-Pesa (or other rail) using payee from GET /api/orders/{o.id}
    await fiatSentHttp(o.id);
    console.log("fiat marked sent");
  } catch (e) {
    console.error("race or validation failed", e.message);
  }
});

const ping = setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: "ping" }));
}, 30_000);

ws.on("close", () => clearInterval(ping));

Full automation example — Python (websockets)

bash
pip install websockets httpx
python
import asyncio
import json
import os

import httpx
import websockets

API = "https://api.rampwire.app"
LP_KEY = os.environ["LP_KEY"]
MAX_KES = 20_000
ALLOWED = {"KES"}


async def claim_http(order_id: int):
    async with httpx.AsyncClient() as c:
        r = await c.post(
            f"{API}/api/orders/{order_id}/claim",
            headers={"Authorization": f"Bearer {LP_KEY}"},
            json={},
            timeout=30.0,
        )
        r.raise_for_status()
        return r.json()


async def fiat_sent_http(order_id: int):
    async with httpx.AsyncClient() as c:
        r = await c.post(
            f"{API}/api/orders/{order_id}/fiat-sent",
            headers={"Authorization": f"Bearer {LP_KEY}"},
            json={},
            timeout=30.0,
        )
        r.raise_for_status()
        return r.json()


async def run_socket():
    uri = f"wss://api.rampwire.app/ws/orders?lp_key={LP_KEY}&currencies=KES"
    async with websockets.connect(uri) as ws:
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("type") != "new_order":
                continue
            o = msg["order"]
            if o["currency"] not in ALLOWED:
                continue
            if o["amount_fiat"] >= MAX_KES:
                continue
            try:
                print("claim", await claim_http(o["id"]))
                print("fiat_sent", await fiat_sent_http(o["id"]))
            except Exception as e:
                print("claim race failed", e)


async def main():
    while True:
        try:
            await run_socket()
        except Exception:
            await asyncio.sleep(2)


asyncio.run(main())

Error handling

Reconnection

Network drops fire close. Reconnect with exponential backoff (for example 1s, 2s, 4s, cap 30s) and expect a fresh init.

Heartbeat

Send { "type":"ping" } about every 30s so proxies do not drop the socket.

Claim race conditions

Treat 409 on HTTP claim or failures as normal when multiple LPs compete. Optionally listen for order_taken (WebSocket claims only).

Connection limit

The 5-socket cap returns HTTP 429 before upgrade. Close stale tooling or consolidate listeners.


Security

  • Never expose lp_key in client-side code or public repos.
  • Run automation on infrastructure you control; inject secrets via environment or a secret manager.
  • Always use wss:// in production.

See also

Rampwire — fiat–crypto infrastructure