Appearance
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_keyquery 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 JSONerror).
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¤cies=KES,NGNcurrencies: 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 wsjavascript
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)}¤cies=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 withstatus = "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}/claimmay not trigger this broadcast. Always treat claim success as authoritative only after your own HTTP response (or your own WSclaimedmessage 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}/claimwithAuthorization: Bearer {lp_key}so collateral locks andlp_receive_addressare 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 claimed → fiat_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:
| Field | Type | Notes |
|---|---|---|
id | number | Primary key / order_id elsewhere |
amount_fiat | number | Local fiat amount |
currency | string | e.g. KES, NGN |
amount_usd | number | USD notional used internally |
crypto_symbol | string | e.g. usdt, xrp |
crypto_chain | string | e.g. trx, xrpl |
status | string | e.g. open |
created_at | string | ISO 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)}¤cies=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 httpxpython
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}¤cies=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_keyin 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.