Quickstart
First 60 seconds with Repull. Six steps from zero to a live integration with webhooks.
TL;DR
One Bearer token. https://api.repull.dev. Cursor pagination. Idempotency-Key on writes. Steps 1 and 2 need only your API key; connecting a channel comes in step 3.
Get an API key
Mint a key from /dashboard/keys. There is one key type — sk_live_… — and it acts on whatever channels your workspace has connected. Store the key in your secrets manager and expose it as an env var.
# .env REPULL_API_KEY=sk_live_paste_your_key_here
Make your first call
List the workspace's listings — your first authenticated call, no channel connection required yet.
curl "https://api.repull.dev/v1/listings?limit=10" \ -H "Authorization: Bearer $REPULL_API_KEY"
Returns { data, pagination }. Cursor pagination — pass pagination.nextCursor back as cursor=… on the next call to walk the rest.
Connect a channel
For real data, the workspace needs at least one channel connection. Mint a Connect session and redirect the user — the picker UI walks them through the OAuth or credentials flow for whichever channel they choose. Full guide: Connect (multi-channel).
curl -X POST https://api.repull.dev/v1/connect \
-H "Authorization: Bearer $REPULL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"redirectUrl": "https://your-app.com/callback"}'Loop reservations
With a connection in place, list reservations across every connected channel through a single endpoint. Filter by status, date range, listing — the API is provider-agnostic.
curl "https://api.repull.dev/v1/reservations?limit=50" \ -H "Authorization: Bearer $REPULL_API_KEY"
limit caps at 100. Use the cursor pattern from Step 2 to walk the rest. See Get Reservations for the full filter list.
Register a webhook for new bookings
Polling the list endpoint works — but webhooks let you react in seconds. Register an endpoint and pick the events you care about. Repull retries with exponential backoff for 24 hours on any non-2xx response. Full guide: Webhooks.
curl -X POST https://api.repull.dev/v1/webhooks \
-H "Authorization: Bearer $REPULL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/repull",
"events": ["reservation.created", "reservation.updated", "reservation.cancelled"]
}'The response includes secret — store it. You will need it in Step 6 to verify deliveries.
Verify webhook signatures
Every webhook delivery includes an X-Repull-Signature header (t=…,v1=…). Verify it before processing — an unverified payload could come from anywhere. Full guide: Verify webhook signatures.
import crypto from 'node:crypto'
export async function POST(req: Request) {
const raw = await req.text() // the raw body — never re-serialised JSON
const header = req.headers.get('X-Repull-Signature') ?? '' // "t=…,v1=…"
const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')))
const age = Math.floor(Date.now() / 1000) - Number(parts.t)
if (!parts.t || !parts.v1 || !(Math.abs(age) <= 300)) {
return new Response('stale or malformed signature', { status: 401 })
}
const expected = crypto
.createHmac('sha256', process.env.REPULL_WEBHOOK_SECRET!)
.update(`${parts.t}.${raw}`)
.digest('hex')
const a = Buffer.from(expected)
const b = Buffer.from(parts.v1)
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return new Response('invalid signature', { status: 401 })
}
const event = JSON.parse(raw) // event.type, event.data
return new Response('ok')
}Verification recomputes HMAC-SHA256 over `${t}.${raw_body}` and compares it to v1. Use the raw body — re-serialising parsed JSON breaks the hash.
What next
- SDKs — typed clients for the language you actually use.
- Idempotency — safe retries for writes.
- Errors — every code page has a fix walkthrough.
- Using Repull from AI agents — patterns for LLM-driven callers.
- Custom Schemas — reshape responses to match your data model.