Partner API
Version v1/public/*. Breaking changes bump to v2/public/*. Non-breaking additions (new fields, new endpoints, new filter params) are guaranteed within a version.
Authentication
All /v1/public/* endpoints require a bearer API key, made under Partnering → Partner widgets → Developers in your dashboard.
Authorization: Bearer bp_<8char_prefix>_<secret>
The prefix is visible in server logs; the secret is never logged. Rotate keys by creating a new one and revoking the old. Keys are scoped — read is sufficient for the feed, operator detail, and availability endpoints.
Versioning policy
- We version the URL prefix, not headers.
- Within
/v1/public/*, we will add new fields and new endpoints but never remove or rename existing ones. - Breaking changes bump to
/v2/public/*. The older version is supported for at least 90 days after v2 launches. - Deprecations are announced via email to your
contactEmailplus aSunsetresponse header.
Endpoints
/v1/public/listingsCursor-paginated listings feed.
curl -H "Authorization: Bearer bp_..." \ "https://<host>/v1/public/listings?category=fishing&limit=50"
Response
{
"listings": [
{
"id": "...",
"slug": "half-day-charter",
"name": "Half-Day Charter",
"category": "fishing",
"subcategoryId": "...",
"operator": { "id": "...", "slug": "demo", "name": "Demo Operator" },
"detailUrl": "https://<host>/products/..."
}
],
"nextCursor": "..."
}/v1/public/listings/:idFull listing detail — variants, addons, media, reviews aggregate, location, book URL.
/v1/public/operators/:slugOperator profile: name, bio, social links.
/v1/public/operators/:slug/listings.jsonDenormalized per-operator feed — every active listing with full detail. Designed for periodic aggregator polling.
/v1/public/availability/batchQuery params: listingIds[] + from + to. Returns starting price per listing + availability flag.
Webhooks
When you set a webhook URL under Partnering → Partner widgets → Developers, we POST signed events for:
booking.confirmed— a booking referred by this partner is now confirmed.booking.cancelled— a confirmed booking has been cancelled.booking.held— hold created but not yet confirmed.review.published— a new public review on an attributed booking.
Each delivery includes a signature header:
X-Partner-Signature: t=1713827100,v1=<hex_hmac_sha256>
Verify by computing HMAC-SHA256(webhookSecret, `$${t}.$${rawBody}`) and comparing in constant time. Reject if |now - t| > 300 seconds.
Backoff schedule: 1m, 5m, 30m, 2h, 12h. We give up at 24 hours — you can retry manually from Developers in your partner dashboard.
# Node.js verifier
import { createHmac, timingSafeEqual } from 'crypto';
const [t, v1] = header.split(',').map(p => p.split('=')[1]);
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) throw new Error('stale');
const expected = createHmac('sha256', secret)
.update(`${t}.${rawBody}`)
.digest('hex');
if (!timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(v1, 'hex'))) {
throw new Error('bad signature');
}Partner script
Your script carries every trip from every operator you have agreed terms with, less any you untick under Trips in your script on your Partner widgets page, and in the order you set there. Past that, your site decides what to show.
Your own markup. With the script loaded, BookingEmbed.partnerTrips() resolves to { partner, trips }. Each trip has slug, name, description, category, categories (its own plus any its parts add, so a cast-and-blast trip has both), subcategories, species and techniques (each { slug, name }, primary first, for splitting walleye from bass), startingPriceCents, heroUrl, heroAlt, highlights, tiers, operator (slug, name) and bookUrl, which is already credited to you. Filter, sort and render the trips however you like. Link to bookUrl, or call BookingEmbed.book(slug) to open the booking popup. Pass { ref } to label the placement on every link.
Show each tier's priceLines rather than its bare priceCents: a trip can be priced per person, by party size or as a base plus extra guests, and the lines say which ("1–3 guests: $300 each", "4+ guests: $250 each").
Booking on your own page. To keep guests on your site, put the booking flow in a frame on a page of yours instead of linking out. It sizes itself to its content and is credited to you from a verified website:
<div data-booking-widget data-product="<trip_slug>" data-mode="inline" data-partner="<your_slug>"></div> <script src="https://<host>/embed.js" async></script>
<script src="https://<host>/embed.js" data-partner="<your_slug>"></script>
<script>
BookingEmbed.partnerTrips().then(({ trips }) => {
const hunting = trips.filter((t) => t.categories.includes('hunting'));
const byOperator = Object.groupBy(hunting, (t) => t.operator.name);
// render either; link each trip to t.bookUrl
});
</script>JSON. The same data, open to any origin and with no key, for server-side code:
GET /v1/embed/partners/<your_slug>/listings
No code? Paste the ready-made widget. It shows every trip, grouped by operator. Use data-layout for all_trips or by_category, and data-accent, data-font and data-radius to match your site.
<div data-booking-widget data-partner="<your_slug>"></div> <script src="https://<host>/embed.js" async></script>
Bookings are credited to you when they come from one of your verified websites, or through a signed ?pt= link.
Attribution
A booking is credited to you in one of two ways. A bare ?partner=<slug> on a link credits nothing on its own.
Signed referral links. Make these under Partnering → Partner widgets → Referral links. The pt token is signed and carries your partner id and an optional placement label, so it works from any site or channel. It is remembered for 30 days from the click, and the last click wins.
https://<host>/book/<PRODUCT_SLUG>?pt=<token>
Your script on a verified website. One snippet lists every operator you're connected with; add data-product to show one trip instead, and data-ref for a placement label:
<div data-booking-widget data-partner="<your_slug>"></div> <script src="https://<host>/embed.js" async></script>
A booking is only credited to you when the page it sits on is one of your verified websites. Verification is a <meta name="asra-partner-verification"> tag on your homepage. How verification works.
What a credit means
A credited booking records that it came from you, and which of your sites or links sent it, so you and the operator can both see it. Nothing is paid for it: partnering isn't commission-based. A booking of your own business's trips is never credited to you.
Rate limits
Default: 600 requests per minute per API key. If you need more for batch polling, contact us. The denormalized feed /listings.json is cheap to poll every few minutes.