Developers

API and webhooks

Last updated:

Send gifts, personalize GG Links per prospect, keep contacts in sync and hear about every gift as it’s claimed, accepted, delivered and thanked for. A JSON REST API at https://www.giasgiraffes.com/api/v1 with test keys, scopes, spending limits and idempotent sends, plus signed flat webhooks, a Zapier app and Make. On Pro. Open the Developer hub

1.Quickstart (5 minutes)

  1. In Settings → Developers → API keys, create a key with mode Test and scope Read, draft and send. Copy it (we show it once):
    export GG_KEY=gg_test_…
  2. List gifts up to $50:
    curl "https://www.giasgiraffes.com/api/v1/catalog?max_cents=5000&limit=3" \
      -H "Authorization: Bearer $GG_KEY"
  3. Draft a gift. Nothing is sent; you get the full summary back, including the most it can cost:
    curl https://www.giasgiraffes.com/api/v1/gift-drafts \
      -H "Authorization: Bearer $GG_KEY" -H "Content-Type: application/json" \
      -d '{"recipient": {"name": "Avery Chen", "email": "avery@example.com"},
           "budget_cents": 5000, "note": "Thanks for the intro!", "occasion": "Thank you"}'
  4. Send the draft. A test key never makes a real charge, order or text. The Idempotency-Key means a retry can’t send twice:
    curl https://www.giasgiraffes.com/api/v1/gift-drafts/$DRAFT_ID/send \
      -H "Authorization: Bearer $GG_KEY" -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d '{"confirm": true}'
  5. Check on it (or add a webhook endpoint and hear about it as it happens):
    curl https://www.giasgiraffes.com/api/v1/gifts/$GIFT_ID -H "Authorization: Bearer $GG_KEY"

Prefer a client generator or Postman? Download the OpenAPI spec.

2.Keys, modes and scopes

The API, webhooks and the AI tools server are part of Pro. Create keys in the Developer hub and send one as a Bearer token over https:

curl https://www.giasgiraffes.com/api/v1/gifts \
  -H "Authorization: Bearer gg_live_…"
  • Live keys (gg_live_…) act on your real gifts and contacts. Test keys (gg_test_…) only ever see and create test data, labeled “Test” in the app, and never make real charges, orders or texts. A live key can’t read test data and a test key can’t read live data. A test key’s gifts go out in test mode: they never touch your real cards, and a live key’s gifts never use a test card. GG Links follow the deployment’s payment mode, so a test key can’t create one where payments are live.
  • Scopes: read (Read only), draft (Read and draft: adds drafts and contacts), send (Read, draft and send: adds sending). A request outside the key’s scope gets 403 with code insufficient_scope; the same scopes apply to the key on the AI tools server.
  • Spending limits per key (per gift, per day, per month) count what gifts could cost (their budgets) and are reserved atomically, so parallel requests can’t go over. Your team owner’s monthly AI tools and API cap applies too.
  • Rotate a key to get a new one with the same name, scope, limits and spend: the old key keeps working for 24 hours, then stops. Revoke stops a key at once. Keys can have an expiry date. Every create, rotate, revoke and change is in the key activity log.
EndpointNeeds
GET /api/v1/contactsread
POST /api/v1/contactsdraft
GET /api/v1/giftsread
GET /api/v1/gifts/{id}read
POST /api/v1/giftssend
POST /api/v1/gift-draftsdraft
POST /api/v1/gift-drafts/{id}/sendsend
GET /api/v1/gg-linksread
POST /api/v1/gg-linkssend
POST /api/v1/gg-links/{id}/personalizeread
GET /api/v1/catalogread
GET /api/v1/meread
GET /api/v1/webhooksread
POST /api/v1/webhooksread
DELETE /api/v1/webhooks/{id}read
GET /api/v1/events/sampleread

3.Endpoints

JSON in and out, times in ISO 8601 (UTC), money in cents (USD). Lists take limit (1 to 100, default 50) and offset. Budgets for a their-choice gift: $10, $15, $20, $25, $30, $35, $40, $45, $50, $60, $70, $80, $90, $100, $110, $125, $150, $175, $200, $250, $300, $350. Every field is in the OpenAPI spec.

GET/api/v1/contactsread

List contacts. Your contacts (your team's shared contacts on a team), filtered by tag or email.

POST/api/v1/contactsdraft

Add a contact. Same validation as the app's contact form. A test key adds a test contact.

GET/api/v1/giftsread

List gifts. Gifts you sent, newest first. Add email= for one recipient's gift history.

GET/api/v1/gifts/{id}read

Get a gift. By its id or its ref (the code in the gift link).

POST/api/v1/giftssendIdempotency-Key

Send a gift. Sends now, from your saved card (charged only when they accept). Counts against the key's spending limits and your team's AI and API cap, reserved atomically, so parallel requests can't go over. Send an Idempotency-Key header so a retry never sends twice.

POST/api/v1/gift-draftsdraft

Draft a gift. Nothing is sent. Returns a draft id and the full summary (recipient, gift, note, the most it can cost, the card, the key's remaining limits). Drafts last 24 hours; send one with POST /gift-drafts/{id}/send.

POST/api/v1/gift-drafts/{id}/sendsendIdempotency-Key

Send a draft. Send a draft made with this key. The body must be {"confirm": true}. Spending limits are reserved atomically; send an Idempotency-Key header so a retry never sends twice.

GET/api/v1/gg-linksread

List GG Links. Your GG Links, newest first (open ones, then the last 20 closed): status, claims used of the max, and spend so far.

POST/api/v1/gg-linkssendIdempotency-Key

Create a GG Link. One shareable URL anyone can open to claim one gift within budget_cents. Guardrails: max_claims (default 10; Free plans up to 5), expires_at, one claim per person (email and shipping address), and require_approval. Each claim is charged only when it's accepted; the key's spending limits count max_claims × budget_cents, reserved atomically. Personal links (type "single") were retired: send a gift with POST /gifts. Send recipient_email, recipient_name, recipient_company and campaign to get personalized_url back for that prospect; creating a link counts toward the plan's active-link cap (Free: 1), personalizing never does.

POST/api/v1/gg-links/{id}/personalizeread

Personalize a GG Link per prospect. The link with ?e=<email>&n=<first name>&c=<campaign>&co=<company> filled in for one prospect ({"recipient_email", "recipient_name", "recipient_company", "campaign"}) or up to 500 ({"recipients": [...]}, with an optional "campaign" for all of them). Nothing is created, counted or stored: one link serves a whole list, so a Free plan's one active link works for every prospect. The claim page prefills their email (the 6-digit code still verifies it), greets them by first name, prefills Company when the link asks for it, and the campaign tag lands on the claim and its gift. Only an active link of this key's account (and mode) can be personalized.

GET/api/v1/catalogread

List the catalog. Gifts you can send, with what each costs if accepted, and the budgets a their-choice gift can use.

GET/api/v1/meread

The key's account and scope. The account email, the key's mode (live or test) and scope. Nothing changes; a good first call to check a key.

GET/api/v1/webhooksread

List webhook endpoints. The endpoints this key's mode delivers to, newest first (the same list as Developers → Webhooks).

POST/api/v1/webhooksread

Subscribe an endpoint. Add an https endpoint for one or more events (Zapier's REST hook subscribe: one event per hook). The endpoint takes the key's mode, so a test key only ever sends test data to it. The answer carries the signing secret once; every delivery is signed with it (GG-Signature). Up to 10 endpoints per account; private and local addresses are refused.

DELETE/api/v1/webhooks/{id}read

Remove an endpoint. Unsubscribe (Zapier's REST hook unsubscribe). Its signing secret is deleted; pending deliveries to it are dropped.

GET/api/v1/events/sampleread

A sample delivery for an event. One made-up delivery body for `type` (exactly the shape a real delivery has; `data.test` follows the key's mode). Nothing real is read or sent.

Send now, in one call

curl https://www.giasgiraffes.com/api/v1/gifts \
  -H "Authorization: Bearer $GG_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1234-thanks" \
  -d '{"contact_id": "…", "budget_cents": 5000, "note": "Thank you!"}'

Create a GG Link

One shareable link anyone can open to claim one gift within your budget (they never see a price). You set the guardrails: max_claims (default 10; Free plans up to 5), an optional expires_at, one_per (person, or also one per company email domain) and require_approval (each claim waits for your Approve or Skip). Whoever claims confirms their email with a 6-digit code we send them, typed on the same page, and then picks their gift. One claim per person is checked by that verified email and by shipping address. Each claim is charged only when it’s accepted, and the key’s spending limits count max_claims × budget_cents. Older requests with "type": "team" still work; "type": "single" (a personal link for one named person) was retired on October 19, 2026 and returns 422: send that person a gift with POST /gifts instead.

curl https://www.giasgiraffes.com/api/v1/gg-links \
  -H "Authorization: Bearer $GG_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: workshop-oct-24" \
  -d '{"budget_cents": 5000, "max_claims": 10, "expires_at": "2026-10-31T23:59:59Z",
       "require_approval": false, "headline": "Thanks for coming!", "message": "Pick anything you like."}'
# → 201 {"data": {"id": "…", "url": "https://giasgiraffes.com/l/…", "max_claims": 10, "max_cost_cents": 50000,
#   "summary": "Up to 10 people can each choose one gift up to $50. The link ends Oct 31, 2026. Your card is charged only when someone accepts.", …}}

GET /gg-links lists your links with status (active, paused, expired, full or closed), claims_used of max_claims, waiting_for_approval, accepted and spent_cents.

Personalize a link per prospect

One GG Link serves a whole outbound list. The claim page reads three query params, ?e=<email>&n=<first name>&c=<campaign>: it prefills the prospect’s email (the 6-digit code still verifies it), greets them by first name, and stores the campaign tag on the claim and the gift it becomes. You can append them yourself, or let us build the URL: send recipient_email, recipient_name and campaign with POST /gg-links and the answer carries personalized_url next to url, or call POST /gg-links/{id}/personalize for any existing active link, with one recipient or a recipients batch of up to 500. Personalizing creates nothing, stores nothing (prospects aren’t added to your contacts) and uses no link slot or spending limit: only creating a link counts toward your plan’s active links (Free: 1, with up to 5 claims; Pro: unlimited), so a Free account personalizes its one link for every prospect. Values are cleaned the way the claim page cleans them (a name keeps letters only; a tag keeps letters, digits, spaces, _ . -), and a recipient without a valid email comes back with personalized_url: null and a note. A read key is enough; nothing here spends.

# At create time: the first prospect's URL comes back with the link
curl https://www.giasgiraffes.com/api/v1/gg-links \
  -H "Authorization: Bearer $GG_KEY" -H "Content-Type: application/json" \
  -d '{"budget_cents": 5000, "max_claims": 10, "recipient_email": "pat@acme.com", "recipient_name": "Pat", "campaign": "Q4 outbound"}'
# → 201 {"data": {"url": "https://giasgiraffes.com/l/q4-outbound-k7m3q9xw",
#   "personalized_url": "https://giasgiraffes.com/l/q4-outbound-k7m3q9xw?e=pat%40acme.com&n=Pat&c=Q4%20outbound", …}}

# Any time after: one prospect…
curl https://www.giasgiraffes.com/api/v1/gg-links/$LINK_ID/personalize \
  -H "Authorization: Bearer $GG_KEY" -H "Content-Type: application/json" \
  -d '{"recipient_email": "sam@northwind.com", "recipient_name": "Sam", "campaign": "Q4 outbound"}'
# → 200 {"data": {"personalized_url": "https://giasgiraffes.com/l/q4-outbound-k7m3q9xw?e=sam%40northwind.com&n=Sam&c=Q4%20outbound",
#   "recipient_email": "sam@northwind.com", "recipient_name": "Sam", "campaign": "Q4 outbound", "note": null, "link_id": "…", "url": "…"}}

# …or a batch (up to 500), with one campaign for all of them
curl https://www.giasgiraffes.com/api/v1/gg-links/$LINK_ID/personalize \
  -H "Authorization: Bearer $GG_KEY" -H "Content-Type: application/json" \
  -d '{"campaign": "Q4 outbound", "recipients": [{"recipient_email": "sam@northwind.com", "recipient_name": "Sam"},
       {"recipient_email": "jo@contoso.com", "recipient_name": "Jo"}]}'
# → 200 {"data": [{"personalized_url": "…?e=sam%40northwind.com&n=Sam&c=Q4%20outbound", …}, {…}], "total": 2, "linked": 2, …}

No code? On GG Links, Personalize a prospect CSV on any active link takes a CSV with an email column (plus first name and campaign columns if you have them, matched by header) and gives it back with a gg_link column, up to 5,000 rows at a time. The file is read in your browser and never stored.

4.Idempotent sends

Send an Idempotency-Key header (any unique string up to 255 characters; a UUID is ideal) on POST /gifts and POST /gift-drafts/{id}/send. For 24 hours:

  • a retry with the same key and body returns the first response (with Idempotent-Replayed: true) and sends nothing;
  • the same key with a different body is refused (422 idempotency_key_reused);
  • while the first request is still running, a repeat gets 409 idempotency_in_progress;
  • a 5xx isn’t remembered, so you can retry it with the same key.

Keys are per API key (a rotated key keeps its predecessor’s).

5.Errors and rate limits

Errors are JSON with the HTTP status, a stable code and a message you can show a person:

{ "error": { "status": 403, "code": "insufficient_scope", "message": "This key is read only; POST /api/v1/gifts needs read, draft and send access…" } }
400invalid_requestThe request didn’t parse, a field is the wrong type, or the Idempotency-Key is malformed
401unauthorizedMissing, malformed, expired or revoked key
402card_requiredNo saved card: add one in the app (you’re charged only when a gift is accepted)
403forbidden / insufficient_scopeNot on Pro, or the key’s scope doesn’t allow this endpoint
404not_foundNot found, not yours, or the other mode’s (a test key can’t see live data)
409conflict / live_mode_unavailable / idempotency_in_progressA contact with that email exists; a live key on a deployment running Stripe test keys; or the same Idempotency-Key is still running
422unprocessable / limit_exceeded / idempotency_key_reusedValid JSON we can’t act on (for example over a spending limit), or an Idempotency-Key reused with a different request
429rate_limitedMore than 120 requests a minute on this key (see Retry-After)
500server_errorSomething broke on our side; it’s safe to retry with the same Idempotency-Key

A send over your monthly team budget returns 202 with status: "pending_approval": nothing is sent until an owner or admin approves it.

smart_link_id is the GG Link the gift was claimed from, or null. GG Links were called Smart Links before October 2026; the field name stays the same so existing integrations keep working.

Each key can make 120 requests a minute. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining; past the limit you get 429 with Retry-After (seconds). The Developer hub’s Usage tab shows requests, errors and rate-limit hits per key for the last 30 days.

6.Webhooks

Add an https endpoint in Developers → Webhooks (or with POST /webhooks), choose its events and whether it’s live or test (test endpoints get events for test data only). We POST JSON to it as things happen. Each endpoint has its own signing secret, shown once and rotatable.

Every body is { id, type, created, data }, and data is flat: snake_case keys, ISO times, ids and URLs one level deep, so Zapier, Make or a spreadsheet can map them without digging (recipient_first_name, recipient_email, campaign, link_id, gift_url…). Gift events also carry the full gift object under data.gift. gift.claimed is a GG Link claim (their email verified; claim_state says whether the gift went out or waits for your approval); for a gift you sent to one person the matching signal is gift.accepted. Nothing in a payload shows a recipient a price: budgets and charges are your side only.

gift.sent A gift link was created (sent, or ready to share).
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.sent",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "sent",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 0,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": null,
    "accepted_at": null,
    "shipped_at": null,
    "delivered_at": null,
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "sent",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 0,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": null,
      "accepted_at": null,
      "shipped_at": null,
      "delivered_at": null,
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    }
  }
}
gift.opened The recipient opened their gift for the first time.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.opened",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "opened",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 0,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": "2026-10-15T18:40:03.000Z",
    "accepted_at": null,
    "shipped_at": null,
    "delivered_at": null,
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "opened",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 0,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": "2026-10-15T18:40:03.000Z",
      "accepted_at": null,
      "shipped_at": null,
      "delivered_at": null,
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    }
  }
}
gift.claimed Someone claimed a gift from one of your GG Links (their email verified). For a gift you sent to one person, the matching signal is gift.accepted.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.claimed",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a",
    "gift_ref": "Lm3Np7Qr9St1Uv5Wx8Yz2A",
    "gift_url": "https://www.giasgiraffes.com/g/Lm3Np7Qr9St1Uv5Wx8Yz2A",
    "gift_status": "sent",
    "recipient_name": "Pat Prospect",
    "recipient_first_name": "Pat",
    "recipient_email": "pat@acme.example",
    "occasion": null,
    "campaign": "Q4 outbound",
    "link_id": "7a3b9c1d-2e4f-4a5b-8c6d-0e1f2a3b4c5d",
    "contact_id": null,
    "budget_cents": 5000,
    "charged_cents": 0,
    "sent_at": "2026-10-19T15:21:40.000Z",
    "opened_at": null,
    "accepted_at": null,
    "shipped_at": null,
    "delivered_at": null,
    "test": false,
    "claim_id": "c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f",
    "claim_state": "sent",
    "claimed_at": "2026-10-19T15:21:40.000Z",
    "link_name": "Q4 outbound",
    "link_url": "https://www.giasgiraffes.com/l/q4-outbound-k7m3q9xw",
    "recipient_company": "Acme",
    "recipient_title": "VP Sales",
    "gift": {
      "id": "9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a",
      "ref": "Lm3Np7Qr9St1Uv5Wx8Yz2A",
      "link": "https://www.giasgiraffes.com/g/Lm3Np7Qr9St1Uv5Wx8Yz2A",
      "status": "sent",
      "mode": "choice",
      "recipient": {
        "name": "Pat Prospect",
        "email": "pat@acme.example"
      },
      "occasion": null,
      "budget_cents": 5000,
      "charged_cents": 0,
      "smart_link_id": "7a3b9c1d-2e4f-4a5b-8c6d-0e1f2a3b4c5d",
      "automation_id": null,
      "contact_id": null,
      "campaign": "Q4 outbound",
      "created_at": "2026-10-19T15:21:40.000Z",
      "opened_at": null,
      "accepted_at": null,
      "shipped_at": null,
      "delivered_at": null,
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    }
  }
}
gift.accepted They picked a gift and accepted it.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.accepted",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "accepted",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 4500,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": "2026-10-15T18:40:03.000Z",
    "accepted_at": "2026-10-15T18:44:57.000Z",
    "shipped_at": null,
    "delivered_at": null,
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "accepted",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 4500,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": "2026-10-15T18:40:03.000Z",
      "accepted_at": "2026-10-15T18:44:57.000Z",
      "shipped_at": null,
      "delivered_at": null,
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    }
  }
}
gift.shipped The brand shipped it.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.shipped",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "shipped",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 4500,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": "2026-10-15T18:40:03.000Z",
    "accepted_at": "2026-10-15T18:44:57.000Z",
    "shipped_at": "2026-10-17T15:10:00.000Z",
    "delivered_at": null,
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "shipped",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 4500,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": "2026-10-15T18:40:03.000Z",
      "accepted_at": "2026-10-15T18:44:57.000Z",
      "shipped_at": "2026-10-17T15:10:00.000Z",
      "delivered_at": null,
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    }
  }
}
gift.delivered It was delivered.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.delivered",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "delivered",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 4500,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": "2026-10-15T18:40:03.000Z",
    "accepted_at": "2026-10-15T18:44:57.000Z",
    "shipped_at": "2026-10-17T15:10:00.000Z",
    "delivered_at": "2026-10-19T20:31:00.000Z",
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "delivered",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 4500,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": "2026-10-15T18:40:03.000Z",
      "accepted_at": "2026-10-15T18:44:57.000Z",
      "shipped_at": "2026-10-17T15:10:00.000Z",
      "delivered_at": "2026-10-19T20:31:00.000Z",
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    }
  }
}
gift.thank_you The recipient sent you a thank-you note.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.thank_you",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "delivered",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 4500,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": "2026-10-15T18:40:03.000Z",
    "accepted_at": "2026-10-15T18:44:57.000Z",
    "shipped_at": "2026-10-17T15:10:00.000Z",
    "delivered_at": "2026-10-19T20:31:00.000Z",
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "delivered",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 4500,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": "2026-10-15T18:40:03.000Z",
      "accepted_at": "2026-10-15T18:44:57.000Z",
      "shipped_at": "2026-10-17T15:10:00.000Z",
      "delivered_at": "2026-10-19T20:31:00.000Z",
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    },
    "message": "This made my week. Thank you!",
    "thanked_at": "2026-10-20T13:02:45.000Z"
  }
}
gift.expired The link expired unredeemed (nothing was charged).
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.expired",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "cancelled",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 0,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": null,
    "accepted_at": null,
    "shipped_at": null,
    "delivered_at": null,
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "cancelled",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 0,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": null,
      "accepted_at": null,
      "shipped_at": null,
      "delivered_at": null,
      "cancelled_at": "2026-11-14T17:02:11.000Z",
      "expires_at": "2026-11-14T17:02:11.000Z",
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    }
  }
}
gift.payment_failed Your card was declined when they accepted (data.final: true once the gift is cancelled for it).
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "gift.payment_failed",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "accepted",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 0,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": "2026-10-15T18:40:03.000Z",
    "accepted_at": "2026-10-15T18:44:57.000Z",
    "shipped_at": null,
    "delivered_at": null,
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "accepted",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 0,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": "2026-10-15T18:40:03.000Z",
      "accepted_at": "2026-10-15T18:44:57.000Z",
      "shipped_at": null,
      "delivered_at": null,
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    },
    "final": false
  }
}
video.reply_received The recipient's thank-you video is ready to watch.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "video.reply_received",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "gift_id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
    "gift_ref": "q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_url": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
    "gift_status": "delivered",
    "recipient_name": "Avery Chen",
    "recipient_first_name": "Avery",
    "recipient_email": "avery@northwind.example",
    "occasion": "Thank you",
    "campaign": null,
    "link_id": null,
    "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "budget_cents": 5000,
    "charged_cents": 4500,
    "sent_at": "2026-10-15T17:02:11.000Z",
    "opened_at": "2026-10-15T18:40:03.000Z",
    "accepted_at": "2026-10-15T18:44:57.000Z",
    "shipped_at": "2026-10-17T15:10:00.000Z",
    "delivered_at": "2026-10-19T20:31:00.000Z",
    "test": false,
    "gift": {
      "id": "5f0c2a1e-8b7d-4c9a-a1f3-2d6e9b8c7a10",
      "ref": "q8Zr2mT4VxK1nP0aBcDeFg",
      "link": "https://www.giasgiraffes.com/g/q8Zr2mT4VxK1nP0aBcDeFg",
      "status": "delivered",
      "mode": "choice",
      "recipient": {
        "name": "Avery Chen",
        "email": "avery@northwind.example"
      },
      "occasion": "Thank you",
      "budget_cents": 5000,
      "charged_cents": 4500,
      "smart_link_id": null,
      "automation_id": null,
      "contact_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
      "campaign": null,
      "created_at": "2026-10-15T17:02:11.000Z",
      "opened_at": "2026-10-15T18:40:03.000Z",
      "accepted_at": "2026-10-15T18:44:57.000Z",
      "shipped_at": "2026-10-17T15:10:00.000Z",
      "delivered_at": "2026-10-19T20:31:00.000Z",
      "cancelled_at": null,
      "expires_at": null,
      "test": false,
      "items": [
        {
          "id": "8a1f6c2e-3b4d-4e5f-9a6b-7c8d9e0f1a2b",
          "title": "Explorer Coffee Box",
          "status": "ordered",
          "shipped_at": null,
          "delivered_at": null
        }
      ]
    },
    "video_id": "2b7e1c9a-4d3f-4e5a-9b8c-7d6e5f4a3b2c",
    "duration_seconds": 23,
    "ready_at": "2026-10-20T13:10:02.000Z",
    "watch_url": "https://www.giasgiraffes.com/app/gifts?gift=q8Zr2mT4VxK1nP0aBcDeFg"
  }
}
meeting.booked A prospect booked a meeting with you (Meetings).
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "meeting.booked",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "meeting_id": "mtg_3f2a9c8e7d6b5a4f",
    "provider": "google",
    "title": "Intro call: Acme × Northwind",
    "starts_at": "2026-10-27T17:00:00.000Z",
    "ends_at": "2026-10-27T17:30:00.000Z",
    "timezone": "America/New_York",
    "join_url": "https://meet.google.com/abc-defg-hij",
    "attendee_name": "Pat Prospect",
    "attendee_email": "pat@acme.example",
    "attendee_company": "Acme",
    "contact_id": null,
    "gift_id": "9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a",
    "link_id": "7a3b9c1d-2e4f-4a5b-8c6d-0e1f2a3b4c5d",
    "campaign": "Q4 outbound",
    "booked_at": "2026-10-20T14:05:12.000Z",
    "canceled_at": null,
    "test": false,
    "attendee_first_name": "Pat"
  }
}
meeting.canceled A booked meeting was canceled.
{
  "id": "8c1d0b0e-4a5f-4b1e-9e6a-0f3c2b1a9d87",
  "type": "meeting.canceled",
  "created": "2026-10-15T18:44:58.000Z",
  "data": {
    "meeting_id": "mtg_3f2a9c8e7d6b5a4f",
    "provider": "google",
    "title": "Intro call: Acme × Northwind",
    "starts_at": "2026-10-27T17:00:00.000Z",
    "ends_at": "2026-10-27T17:30:00.000Z",
    "timezone": "America/New_York",
    "join_url": "https://meet.google.com/abc-defg-hij",
    "attendee_name": "Pat Prospect",
    "attendee_email": "pat@acme.example",
    "attendee_company": "Acme",
    "contact_id": null,
    "gift_id": "9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a",
    "link_id": "7a3b9c1d-2e4f-4a5b-8c6d-0e1f2a3b4c5d",
    "campaign": "Q4 outbound",
    "booked_at": "2026-10-20T14:05:12.000Z",
    "canceled_at": "2026-10-26T09:14:00.000Z",
    "test": false,
    "attendee_first_name": "Pat"
  }
}

Headers: GG-Signature, GG-Event and GG-Delivery (the delivery id, the same across retries). Send test event posts a test.ping to one endpoint.

7.Verifying signatures

GG-Signature is t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body> with the endpoint’s signing secret (whsec_…). Check it against the raw body before parsing, and reject timestamps more than five minutes old.

import crypto from "node:crypto";

// rawBody: the request body exactly as received (before JSON.parse).
export function verify(secret, header, rawBody) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // older than 5 minutes
  const want = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return parts.v1?.length === want.length && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(want));
}

8.Retries, the delivery log and auto-disable

Reply with any 2xx within 10 seconds. Anything else (or a timeout) is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 24 hours, then marked failed. Redirects aren’t followed; private and local addresses are refused.

Each endpoint’s delivery log shows every delivery with its status code, response time, attempts, next retry and payload, and a Resend button. Deliveries can arrive more than once or out of order: use the delivery id to skip duplicates and the gift’s timestamps for order.

An endpoint whose last 10 attempts all failed, over at least 24 hours, is turned off and you’re told in the app. Fix it, choose Resume, and resend what it missed from the delivery log.

9.Zapier

The Gia’s Giraffes Zapier app connects with an API key (no OAuth): in a Zap, pick Gia’s Giraffes, paste a key, done. A test key makes a test connection whose triggers only ever see test data and whose actions never charge, order or text.

  • Triggers (instant, REST hooks): Gift Claimed (gift.claimed), Gift Delivered (gift.delivered), Thank-You Received (gift.thank_you), Meeting Booked (meeting.booked), Meeting Canceled (meeting.canceled) and Video Reply Received (video.reply_received). Turning a Zap on subscribes an endpoint for that one event (you’ll see it in Developers → Webhooks with a Zapier badge); turning it off removes it. Each delivery’s signature is verified before the Zap runs.
  • Actions: Create GG Link (budget, max claims, expiry, approve each claim, and the first prospect’s personalized URL), Personalize GG Link for a Contact (pick one of your active links, map the prospect’s email, first name and a campaign tag, get personalized_url back), and Send Gift to a Contact (their choice up to a budget or one item, your note, email or a link; map a CRM record id into Idempotency key so a replayed Zap never sends twice).

Under the hood it’s this API: GET /me to test the key, POST /webhooks and DELETE /webhooks/{id} to subscribe and unsubscribe, GET /events/sample for the sample a Zap editor shows. Step-by-step recipes for Apollo, Instantly, Smartlead, lemlist, Outreach, Salesloft, HubSpot Sequences and Gmail/Outlook mail merge are in docs/recipes/ of the repository.

10.Make (Integromat)

Make needs no app of ours: its Custom webhook module gives you a URL, and our webhooks post flat JSON to it.

  1. In Make, add Webhooks → Custom webhook to a scenario, click Add, name it and copy the URL (https://hook.…make.com/…).
  2. In Developers → Webhooks, add that URL as an endpoint, tick the events you want (for outbound: gift.claimed, gift.delivered, gift.thank_you, meeting.booked, meeting.canceled, video.reply_received), choose live or test, and copy the signing secret. Endpoints on Make’s hook hosts show a Make badge.
  3. Back in Make the webhook says “Determine data structure”: click Send test event on the endpoint (a test.ping), or paste a sample from this page into Make’s “Sample data” box, so every field becomes mappable. Fields arrive as data.recipient_email, data.campaign, data.gift_url and so on; filter on type when one endpoint carries several events.
  4. Answer 2xx. A Custom webhook replies 200 “Accepted” on its own. If you add a Webhook response module (for example to answer early before a slow CRM step), keep its status at 200. Anything else, or no answer in 10 seconds, is retried with backoff.
  5. Check the signature (recommended): the request’s GG-Signature header is t=…,v1=…. In Make, turn on Show advanced settings → Get request headers on the webhook, then verify with a Crypto → HMAC module: HMAC-SHA256 of {{t}}.{{raw body}} with the secret (raw body: enable JSON pass-through on the webhook so the body isn’t re-serialized), compare hex to v1 in a Filter, and reject when t is more than five minutes old. The same check in code is under Verifying signatures.

To act from Make (create a link, personalize it, send a gift), use the HTTP → Make a request module against the endpoints above with the header Authorization: Bearer gg_…; for example POST https://www.giasgiraffes.com/api/v1/gg-links/{id}/personalize with {"recipient_email": "{{email}}", "recipient_name": "{{first name}}", "campaign": "Q4"}.

11.AI tools and help

Using Claude, ChatGPT, Cursor or Claude Code? Connect them to our MCP server: it uses the same drafts, scopes and spending limits. Everything else lives in the Developer hub. Questions: hello@giasgiraffes.com.