1. Authentication
Partner endpoints authenticate with a partner key sent in the X-Amblify-Partner-Key header. There are no OAuth flows or session cookies: the key identifies which partner is calling, and everything you create is attributed to that partner.
Getting a key
Partner keys are issued by the Amblify team, one per partner integration. Contact us with your company, the site or platform you're integrating and a technical contact, and we'll send you a key over a private channel. If a key ever leaks, contact us and we'll rotate it — the old key stops working as soon as the new one is issued.
- Keep it server-side. The key carries write access for your whole integration. Never ship it in browser JavaScript, a mobile app bundle or a public repository.
- Send it on every request. The API is stateless, so each call to a partner endpoint must carry the header. Header names are case-insensitive.
- Always use HTTPS. Requests are JSON (
Content-Type: application/json, max body 256 KB) and responses are JSON with anokboolean.
Request
POST /api/campaigns/external HTTP/1.1
Host: amblify-backend.guud.ai
Content-Type: application/json
X-Amblify-Partner-Key: pk_live_your_partner_key
Responses when the key is wrong
A missing or unknown key is rejected with 401 before the request body is looked at:
{ "ok": false, "error": "partner key required" }
{ "ok": false, "error": "invalid partner key" }
Try it
A quick way to check your key: send an empty body to the campaign endpoint. A valid key gets past authentication and returns a 400 validation error; a bad key returns 401.
curl -i -X POST https://amblify-backend.guud.ai/api/campaigns/external \
-H "Content-Type: application/json" \
-H "X-Amblify-Partner-Key: $AMBLIFY_PARTNER_KEY" \
-d '{}'
{ "ok": false, "error": "store_host must be a hostname (max 190 chars)" }
2. POST /api/campaigns/external
Creates or updates a commission bounty for one of your stores, or for a single product in it. Creators who claim it earn commission_pct of every sale their traffic makes. Call it whenever a merchant turns on creator commissions, changes the rate or product details, or pauses the offer.
The call is idempotent: a bounty is keyed by store_host (plus product_id for a product listing). Posting the same key again updates the existing bounty instead of creating a duplicate. If no Amblify brand account exists for brand_email, one is created for the merchant; they can set a password later with “Forgot your password?” on the brand log-in page.
Request body
| Field | Type | Notes |
|---|---|---|
store_host | string, required | The store's hostname, e.g. shop.example.com. A full URL is accepted and reduced to its host. Max 190 chars. |
product_id | string, optional | Your product id, for a single-product bounty. No spaces or colons, max 80 chars. Omit for a whole-store bounty. |
title | string, required | Shown on the bounty card. Max 190 chars. |
product_url | URL, required | Where creators send traffic (http/https, max 1000 chars). Creators' links add ?amb=<handle> to it. |
product_image_url | URL, optional | Product image for the card (http/https, max 1000 chars). |
commission_pct | number, required | Greater than 0, at most 100. Rounded to 2 decimals. |
brand_email | email, required | The merchant's email. Finds or creates their brand account. |
brand_name | string, optional | Display name for a newly created brand (defaults to store_host). Max 120 chars. |
instructions | string, optional | What creators should say or show. Max 20,000 chars. |
follower_min | integer, optional | Minimum followers to claim. Default 0. |
status | string, optional | active (default), paused or closed. |
{
"store_host": "shop.example.com",
"product_id": "sku-1042",
"title": "Cloud Knit Hoodie",
"product_url": "https://shop.example.com/products/cloud-knit-hoodie",
"product_image_url": "https://shop.example.com/img/cloud-knit-hoodie.jpg",
"commission_pct": 15,
"brand_email": "[email protected]",
"brand_name": "Example Apparel",
"instructions": "Show the hoodie on and mention the free returns.",
"follower_min": 1000,
"status": "active"
}
Success response
201 Created when the bounty is new, 200 OK when an existing one was updated (created tells you which). brand.created is true when this call also created the merchant's brand account.
{
"ok": true,
"created": true,
"campaign": {
"campaign_id": 482,
"brand_user_id": 1937,
"source": "guuds",
"external_id": "shop.example.com:sku-1042",
"title": "Cloud Knit Hoodie",
"type": "commission",
"per_post_rate": 0,
"per_link_rate": 0,
"commission_pct": 15,
"follower_min": 1000,
"image_url": "https://shop.example.com/img/cloud-knit-hoodie.jpg",
"product_url": "https://shop.example.com/products/cloud-knit-hoodie",
"product_image_url": "https://shop.example.com/img/cloud-knit-hoodie.jpg",
"store_host": "shop.example.com",
"blitz_id": null,
"instructions": "Show the hoodie on and mention the free returns.",
"required_inclusions": ["https://shop.example.com/products/cloud-knit-hoodie"],
"status": "active",
"created_at": "2026-10-06T14:02:11.000Z",
"updated_at": "2026-10-06T14:02:11.000Z"
},
"brand": {
"user_id": 1937,
"brand_key": "shop-example-com",
"created": true
}
}
Errors and status codes
| Status | When |
|---|---|
201 | Bounty created. |
200 | Existing bounty (same store_host + product_id) updated. |
400 | Validation failed — error names the field, e.g. "valid brand_email required", "product_url must be a valid http(s) URL (max 1000 chars)", "commission_pct must be greater than 0", "status must be one of: active, paused, closed". |
401 | Partner key missing or invalid (section 1). |
409 | brand_email already belongs to a creator (non-brand) account: "brand_email belongs to an existing non-brand account". Use a different email for the merchant. |
5xx | Something failed on our side. Safe to retry — the call is idempotent. |
Every error body has the same shape:
{ "ok": false, "error": "valid brand_email required" }
cURL
curl -X POST https://amblify-backend.guud.ai/api/campaigns/external \
-H "Content-Type: application/json" \
-H "X-Amblify-Partner-Key: $AMBLIFY_PARTNER_KEY" \
-d '{
"store_host": "shop.example.com",
"product_id": "sku-1042",
"title": "Cloud Knit Hoodie",
"product_url": "https://shop.example.com/products/cloud-knit-hoodie",
"product_image_url": "https://shop.example.com/img/cloud-knit-hoodie.jpg",
"commission_pct": 15,
"brand_email": "[email protected]",
"brand_name": "Example Apparel",
"instructions": "Show the hoodie on and mention the free returns.",
"follower_min": 1000,
"status": "active"
}'
To pause or close the bounty later, post the same store_host / product_id with "status": "paused" or "closed". Claims creators verified before that still pay.
3. POST /api/blitz
Launches a blitz: a time-boxed push in which several products are bundled under one shared commission rate and a deadline, fronted by a large-following creator. The blitz is listed on Amblify like any other bounty until deadline_at, then it ends on its own.
With a partner key, the call launches the blitz on behalf of a creator named in launched_by. That creator is shown as the launcher and is credited for it. Products can be existing Amblify product bounties (campaign_ids) or product URLs (product_urls). Each URL is upserted as a product bounty the same way as POST /api/campaigns/external, at the blitz commission rate.
Request body
| Field | Type | Notes |
|---|---|---|
launched_by | string, required | The launching creator's Amblify handle (e.g. jane) or numeric user id. The account must be active. |
title | string, required | Shown on the blitz card. Max 190 chars. |
commission_pct | number, required | Greater than 0, at most 100. Every bundled product pays this rate during the blitz. |
deadline_at | ISO datetime, required | When the blitz ends, e.g. 2026-10-31T23:59:59Z. A bare date (2026-10-31) means 23:59:59 UTC that day. Must be in the future and within 365 days. |
product_urls | array, optional* | Each entry is a product URL string, or an object { product_url, title?, product_image_url?, product_id?, store_host?, brand_email?, brand_name? }. brand_email is required for a store that has no product bounty on Amblify yet. |
campaign_ids | array of integers, optional* | Ids of existing, active product bounties to include. |
instructions | string, optional | What creators should say or show. Max 20,000 chars. |
status | string, optional | live (default) lists it immediately; draft keeps it hidden from everyone except the launcher. |
* At least one entry across product_urls and campaign_ids, and at most 50 in total.
{
"launched_by": "jane",
"title": "Fall Layers Blitz",
"commission_pct": 20,
"deadline_at": "2026-10-31T23:59:59Z",
"product_urls": [
"https://shop.example.com/products/cloud-knit-hoodie",
{
"product_url": "https://shop.example.com/products/trail-fleece",
"title": "Trail Fleece",
"product_image_url": "https://shop.example.com/img/trail-fleece.jpg",
"brand_email": "[email protected]"
}
],
"instructions": "Post one fit check with both pieces before Oct 31.",
"status": "live"
}
Success response
201 Created. blitz.items lists the bundled product bounties in order, and campaign is the umbrella listing (source: "blitz") that creators see and claim.
{
"ok": true,
"blitz": {
"blitz_id": 37,
"launched_by_user_id": 2210,
"title": "Fall Layers Blitz",
"commission_pct": 20,
"deadline_at": "2026-10-31T23:59:59.000Z",
"status": "live",
"metadata": {
"partner": "guuds",
"launched_via": "partner",
"instructions": "Post one fit check with both pieces before Oct 31."
},
"created_at": "2026-10-06T14:20:03.000Z",
"updated_at": "2026-10-06T14:20:03.000Z",
"items": [
{
"campaign_id": 482,
"source": "guuds",
"external_id": "shop.example.com:cloud-knit-hoodie",
"title": "Cloud Knit Hoodie",
"type": "commission",
"commission_pct": 20,
"product_url": "https://shop.example.com/products/cloud-knit-hoodie",
"store_host": "shop.example.com",
"status": "active"
},
{
"campaign_id": 483,
"source": "guuds",
"external_id": "shop.example.com:trail-fleece",
"title": "Trail Fleece",
"type": "commission",
"commission_pct": 20,
"product_url": "https://shop.example.com/products/trail-fleece",
"product_image_url": "https://shop.example.com/img/trail-fleece.jpg",
"store_host": "shop.example.com",
"status": "active"
}
]
},
"campaign": {
"campaign_id": 484,
"brand_user_id": 2210,
"source": "blitz",
"external_id": "37",
"title": "Fall Layers Blitz",
"type": "commission",
"commission_pct": 20,
"product_url": "https://shop.example.com/products/cloud-knit-hoodie",
"store_host": "shop.example.com",
"blitz_id": 37,
"instructions": "Post one fit check with both pieces before Oct 31.",
"status": "active",
"created_at": "2026-10-06T14:20:03.000Z",
"updated_at": "2026-10-06T14:20:03.000Z"
}
}
Campaign objects are trimmed here for readability. They carry the same fields as in section 2. A draft blitz returns "status": "draft" on both the blitz and its umbrella campaign.
Errors and status codes
| Status | When |
|---|---|
201 | Blitz created. |
400 | Validation failed, e.g. "launched_by (ambassador uname or user id) required for partner launches", "deadline_at must be in the future", "at least one campaign_ids entry or product_urls entry required", "a blitz bundles at most 50 products", or a product_urls entry for an unknown store that has no brand_email. |
401 | Partner key missing or invalid (section 1). |
404 | "launched_by does not match an active user", or "campaign 123 not found or not active". |
409 | A brand_email in product_urls belongs to a creator (non-brand) account. |
429 | More than 5 blitzes launched for the same launched_by creator in one day. Wait for the Retry-After header and try again. |
cURL
curl -X POST https://amblify-backend.guud.ai/api/blitz \
-H "Content-Type: application/json" \
-H "X-Amblify-Partner-Key: $AMBLIFY_PARTNER_KEY" \
-d '{
"launched_by": "jane",
"title": "Fall Layers Blitz",
"commission_pct": 20,
"deadline_at": "2026-10-31T23:59:59Z",
"product_urls": [
"https://shop.example.com/products/cloud-knit-hoodie",
{
"product_url": "https://shop.example.com/products/trail-fleece",
"title": "Trail Fleece",
"product_image_url": "https://shop.example.com/img/trail-fleece.jpg",
"brand_email": "[email protected]"
}
],
"instructions": "Post one fit check with both pieces before Oct 31.",
"status": "live"
}'
4. POST /api/track/conversion
Reports a sale a creator drove, from the merchant's server, right after the order is paid. The sale is recorded, and the creator's commission is fixed at the rate of their verified bounty claim on that brand. The brand is invoiced for that commission, and the creator is paid it at month end. The creator's handle comes from the amb value that /track.js stored on the store visit (the ?amb= link parameter).
This endpoint moves money, so each request must be signed with the brand's track_key. That is the per-brand secret (amb_tk_…) shown in the brand dashboard and returned by GET /api/brands/me/track-key. Your partner key alone is not accepted here. Send X-Amblify-Partner-Key as on every partner call, and add the brand signature headers below.
Signing the request
- Payload that is signed: the exact raw bytes of the JSON request body, as sent. The body is not reformatted or re-serialised, there is no timestamp, and no headers are included. Sign the string you put on the wire, and send that same string.
- Secret: the brand's
track_key, used as the HMAC-SHA256 key. It stays on your server and never travels with the request. - Algorithm: HMAC-SHA256, hex-encoded (64 lowercase characters).
- Headers:
X-Amblify-Signature: sha256=<hex>, plusX-Amblify-Brand: <brand_key>, the brand's slug, which tells us whichtrack_keyto verify against.
From a trusted server you can instead send the key itself as Authorization: Bearer <track_key>. The signature is preferred because the key never leaves your server.
Worked signing example
With this track_key (example only) and this exact body (no trailing newline):
track_key = amb_tk_0123456789abcdef0123456789abcdef0123456789abcdef
brand_key = example-apparel
body = {"order_id":"ORDER-12345","amount":49.99,"currency":"USD","amb":"jane"}
HMAC-SHA256(track_key, body) gives:
X-Amblify-Signature: sha256=f5f18db3c38d4563123a5f5a24052082d7ba61d12ec16959829fbc0564e9edd0
To reproduce it:
printf '%s' '{"order_id":"ORDER-12345","amount":49.99,"currency":"USD","amb":"jane"}' \
| openssl dgst -sha256 -hmac 'amb_tk_0123456789abcdef0123456789abcdef0123456789abcdef'
# SHA2-256(stdin)= f5f18db3c38d4563123a5f5a24052082d7ba61d12ec16959829fbc0564e9edd0
const crypto = require('crypto');
const body = JSON.stringify({ order_id: 'ORDER-12345', amount: 49.99, currency: 'USD', amb: 'jane' });
const sig = crypto.createHmac('sha256', process.env.AMBLIFY_TRACK_KEY).update(body).digest('hex');
// send `body` unchanged, with header X-Amblify-Signature: sha256=${sig}
If you change one byte of the body after signing, including whitespace or key order, the request is rejected with 401 bad signature.
Request body
| Field | Type | Notes |
|---|---|---|
order_id | string, required | Your order id, 1–120 chars (letters, digits, . _ : # / -). It is the idempotency key per brand: re-sending the same order returns the original record instead of creating a second one. |
amount | number, required | Order total in major units (dollars), >= 0. |
currency | string, optional | 3-letter code. Default USD. |
amb | string, required | The referring creator's handle, from the amb_ref cookie or the ?amb= parameter. |
campaign_id | integer, optional | The commission bounty to pay under. If omitted, we use the creator's highest-rate verified claim with this brand. |
Success response
201 Created for a new sale. A replay of an order_id we already have returns 200 OK with "duplicate": true and the original record. commission_owed is amount × commission_pct / 100, rounded to the cent. If the creator has no verified commission claim with the brand, the sale is still stored, with attributed: false, commission_owed: 0 and reason: "no_commission_claim". If a sale trips a fraud check, it is stored with review_status: "pending_review" and is not paid until a reviewer approves it.
{
"ok": true,
"duplicate": false,
"conversion": {
"conversion_id": 9182,
"order_id": "ORDER-12345",
"amount": 49.99,
"currency": "USD",
"campaign_id": 482,
"ambassador": "jane",
"ambassador_user_id": 2210,
"brand_user_id": 1937,
"commission_pct": 15,
"commission_owed": 7.5,
"attributed": true,
"review_status": "clear",
"created_at": "2026-10-06T14:31:47.000Z",
"brand_key": "example-apparel",
"points": 49,
"reason": "commission",
"fraud_flag": null,
"held": null
}
}
Errors and status codes
| Status | When |
|---|---|
201 | Sale recorded. |
200 | Same order_id already recorded ("duplicate": true). Nothing new is created. |
400 | Validation failed, e.g. "order_id required (1-120 chars: letters, digits, . _ : # / -)", "amount must be a number >= 0 (dollars)", "amb (ambassador uname) required", "JSON body required". |
401 | "bad signature", "unknown brand", "invalid track_key", or no brand auth headers at all. |
403 | The brand account is suspended or closed. |
404 | "unknown ambassador", or campaign_id is not an active commission bounty of this brand that the creator holds a verified claim on. |
429 | More than 600 conversions per minute for the brand. Wait for the Retry-After header and retry. Nothing was recorded. |
5xx | Something failed on our side. Safe to retry with the same order_id. |
cURL
This signs the body with $AMBLIFY_TRACK_KEY and sends that same string:
BODY='{"order_id":"ORDER-12345","amount":49.99,"currency":"USD","amb":"jane"}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$AMBLIFY_TRACK_KEY" | sed 's/^.*= //')
curl -X POST https://amblify-backend.guud.ai/api/track/conversion \
-H "Content-Type: application/json" \
-H "X-Amblify-Partner-Key: $AMBLIFY_PARTNER_KEY" \
-H "X-Amblify-Brand: example-apparel" \
-H "X-Amblify-Signature: sha256=$SIG" \
--data-raw "$BODY"
4a. POST /api/track/conversion: affiliate events
For the always-on affiliate program, your server reports what happens to a referred customer: the signup, every payment (one-off, or one subscription cycle), and any refund or chargeback. Send these to the same POST /api/track/conversion endpoint. A body with an event field is an affiliate event. A body without one is the purchase report in section 4.
Amounts are in dollars. Every id is your app's own id, never an email or other personal data: 1–120 characters of letters, digits and . _ : # / | + = - (no @).
Authentication
The auth is the same as for a purchase report. Use either form:
Authorization: Bearer <track_key>, from a trusted server.X-Amblify-Brand: <brand_key>plusX-Amblify-Signature: sha256=<hex>, the HMAC-SHA256 of the exact raw body keyed with thetrack_key. See signing the request.
Event bodies
| Event | Fields | Effect |
|---|---|---|
signup | customer_id (required), amb (the creator handle from the amb_ref cookie), occurred_at (optional, ISO time) | Stores the attribution on that customer. The first signup wins, and later ones never move it. |
payment | customer_id (required, the id your signup reported), charge_id (required), amount (required, > 0), currency (optional, default USD), subscription_id (optional), occurred_at (optional) | One one-off charge, or one subscription cycle. A subscription earns for its first 12 months from its first reported charge. |
refund or chargeback | refund_id (required), charge_id (required, the charge being refunded), amount (optional, default the whole remaining charge) | Reverses the matching commission pro rata. After payout, it becomes a negative clawback line that nets against the next payout. |
{"event":"signup","customer_id":"cus_1042","amb":"jane","occurred_at":"2026-11-01T12:00:00Z"}
{"event":"payment","customer_id":"cus_1042","charge_id":"ch_88213","amount":29,"currency":"USD","subscription_id":"sub_551","occurred_at":"2026-11-01T12:05:00Z"}
{"event":"refund","refund_id":"re_301","charge_id":"ch_88213","amount":29}
Idempotency
A signup is idempotent on customer_id, a payment on charge_id, and a refund or chargeback on refund_id, all per brand. A new row returns 201. A replay returns 200 with "duplicate": true and the original record, so retrying after a timeout is always safe.
Responses and outcome values
Every response is {"ok": true, "event": "<event>", ...}. A signup returns recorded, reason and customer. A payment returns charge with an outcome, and a refund returns refund with an outcome.
| Event | Value | Meaning |
|---|---|---|
signup reason | attributed | Attribution stored (201). |
already_attributed | This customer already has a referrer. Returned with duplicate: true, and nothing changes. | |
no_ambassador | No amb was sent, so nothing was stored. | |
unknown_ambassador | No creator has that handle. | |
ambassador_inactive | The creator's account is not active. | |
payment charge.outcome | commission | Earns the brand's commission rate. It is held for the 30-day refund hold, then becomes payable. |
no_attribution | No signup was reported for this customer_id, or it had no referrer. | |
outside_window | The charge (or, for a subscription, its first charge) falls after the customer's attribution window closed. | |
recurring_ended | The subscription is past its 12 earning months. | |
no_active_campaign | The brand has no active affiliate commission. | |
unsupported_currency | Commission is only computed on USD charges. | |
ambassador_inactive | The referring creator's account is not active. | |
refund / chargeback refund.outcome | reversed | The commission was not paid yet and is reduced pro rata. |
clawback | The commission was already paid. A negative line nets against the next payout. | |
no_commission | The charge earned nothing, so there is nothing to reverse. | |
unknown_charge | No payment with that charge_id was reported. The refund is still stored. |
Errors use the same status codes as section 4. 400 covers an unknown event ("event must be signup, payment, refund or chargeback") or a missing or invalid id or amount. 401 means bad auth, 403 means the brand may not report, and 429 means the brand is over 600 per minute.
5. GET /api/linkpage/:uname
Reads a creator's public link page, the data behind amblify.guud.ai/u/<handle>. It includes their profile, personal and curated links, the brand deals they are live on (with tracked links already built) and their page design. Use it to show a creator's Amblify links or deals inside your own product.
The data is public, so the endpoint works without authentication. Partner integrations send X-Amblify-Partner-Key anyway, as on every call. Responses are cached for up to 60 seconds (Cache-Control: public, max-age=60), and requests are limited to 300 per minute per IP.
Path parameter
| Parameter | Type | Notes |
|---|---|---|
uname | string, required | The creator's Amblify handle, without the @. It is case-insensitive and may contain letters, digits, ., _ and -, up to 120 chars. URL-encode it. |
Request
GET /api/linkpage/jane HTTP/1.1
Host: amblify-backend.guud.ai
X-Amblify-Partner-Key: pk_live_your_partner_key
Success response
200 OK. For each deal, tracked_url is the brand link with this creator's ?amb= attached. Link to click_url (relative to the API base) when you want the click counted on the creator's analytics. design holds the page's resolved colours, fonts and a ready stylesheet (also served at theme_css_url), and is trimmed below.
{
"ok": true,
"linkpage": {
"uname": "jane",
"display_name": "Jane Rivera",
"pic": "https://amblify.guud.ai/uploads/avatars/2210.jpg",
"country": "US",
"bio": "Everyday fits and trail gear.",
"socials": { "instagram": "janerivera", "tiktok": "janerivera" },
"links": [
{
"label": "Instagram: @janerivera",
"url": "https://instagram.com/janerivera",
"kind": "instagram",
"click_url": "/api/linkpage/jane/go?url=https%3A%2F%2Finstagram.com%2Fjanerivera"
}
],
"curated_links": {
"personal": [],
"shop": [
{
"linkpage_link_id": 77,
"kind": "shop",
"label": "My hoodie pick",
"url": "https://shop.example.com/products/cloud-knit-hoodie",
"tracked_url": "https://shop.example.com/products/cloud-knit-hoodie?amb=jane",
"sort": 0,
"click_url": "/api/linkpage/jane/go?link_id=77"
}
],
"deal": []
},
"theme": {
"headline": "Jane Rivera (@jane)",
"bio": "Everyday fits and trail gear.",
"accent": "#5b5bf7",
"background": "#ffffff",
"generated_at": null
},
"design": { "preset_id": "clean", "saved": true, "scheme": "light", "css_vars": { "…": "…" }, "css": "…" },
"theme_css_url": "/api/linkpage/jane/theme.css",
"deals": [
{
"campaign_id": 482,
"title": "Cloud Knit Hoodie",
"type": "commission",
"image_url": "https://shop.example.com/img/cloud-knit-hoodie.jpg",
"instructions": "Show the hoodie on and mention the free returns.",
"claim_status": "live",
"verified_at": "2026-10-02T09:12:00.000Z",
"live_at": "2026-10-02T09:12:00.000Z",
"brand": {
"user_id": 1937,
"brand_name": "Example Apparel",
"brand_key": "example-apparel",
"site_url": "https://shop.example.com",
"logo_url": "",
"guud_owned": false
},
"url": "https://shop.example.com/products/cloud-knit-hoodie",
"tracked_url": "https://shop.example.com/products/cloud-knit-hoodie?amb=jane",
"click_url": "/api/linkpage/jane/go?url=https%3A%2F%2Fshop.example.com%2Fproducts%2Fcloud-knit-hoodie%3Famb%3Djane"
}
],
"blocks": [],
"member_since": "2026-03-14T18:05:22.000Z"
}
}
Unknown uname (404)
If no active creator has that handle, the response is 404. That includes a deactivated account and a handle that belongs to a brand. The body is the standard error shape. Treat it as "no link page" and don't retry. A handle with characters outside the allowed set gets a 400 before any lookup.
{ "ok": false, "error": "ambassador not found" }
{ "ok": false, "error": "invalid uname" }
Too many requests from one IP return 429 with a Retry-After header.
cURL
curl https://amblify-backend.guud.ai/api/linkpage/jane \
-H "X-Amblify-Partner-Key: $AMBLIFY_PARTNER_KEY"