Skip to main content

Listing deals

GET /v1/deals returns the deals available to your partner account — the global FounderPass catalog plus any live partner-private deals you've created — with every partner-level visibility rule applied (global rules, partner/partner-type targeting, channel exclusion, and your per-deal toggles). It is partner-scoped: it does not take an end user, so it never applies per-member region, user-type, or eligibility gates. Those are enforced per member at GET /v1/deals/{id}/instructions (and on the hosted end-user listing).

A simple call

curl "https://perks.founderpass.com/v1/deals?page=1&per_page=20" \
-H "Authorization: Bearer $TOKEN"

Narrowing the listing

Pass query parameters to narrow the listing.

curl "https://perks.founderpass.com/v1/deals?\
tier=premium&\
category=cloud-infrastructure" \
-H "Authorization: Bearer $TOKEN"
ParameterTypeNotes
categorystringCategory slug — see GET /v1/categories
tierenumfree or premium — filters by the deal's tier
page, per_pageintegerStandard pagination (per_page max 100)

There's no region or user_type parameter: this endpoint is partner-scoped (see above). To narrow by region, read each deal's available_regions and filter client-side (see the tip below).

:::tip Partner-level visibility + freemium gating are evaluated server-side You don't need to combine filters with your own partner-level access logic. The deal listing already respects the partner-level visibility rules attached to a deal (global hide, partner/partner-type targeting, your per-deal toggles) and your partner's freemium settings. Deals an admin has marked api_excluded never appear in the Partner API on any path (list, detail, or instructions), even though they remain live in the hosted app. Per-member region/user-type/eligibility gates are not applied here — enforce those per member at GET /v1/deals/{id}/instructions. :::

Eligibility criteria on each deal

List and detail responses include the deal's eligibility so you can display requirements or filter on your side:

  • eligibility_requirements — a human-readable string (e.g. "Must be a VC-backed company founded in the last 5 years"). Render it on the deal card or detail page.
  • Structured arrays for programmatic checks — empty means "no constraint on this dimension":
FieldExample values
eligibility_customer_typenew_customers_only, existing_customers_eligible
eligibility_company_stagepre_seed, seed, series_a_plus, smb, enterprise, any
eligibility_company_size1_10, 11_50, 51_200, 200_plus
eligibility_business_typestartup, agency, ecommerce, saas, creator, local_business, any
eligibility_geographyISO-3166-1 alpha-2 country codes — e.g. US, DE, GB (uppercase). Not AudienceRegion names — those apply only to regions / visibility rules, not to eligibility.
eligibility_incorporationregistered_company_required, sole_traders_allowed
eligibility_fundingvc_backed_only, accelerator_only, student_founders, coworking_members, portfolio_companies_only

These are informational on the Partner API — surface them to set expectations, but the offering partner still performs the actual eligibility check at redemption.

Fetching a single deal

curl "https://perks.founderpass.com/v1/deals/{id}?user_tier=premium" \
-H "Authorization: Bearer $TOKEN"

Returns the full deal object. Optional user_tier query param enforces tier gating — if the user is free and the deal is premium, you'll get a 403 with reason: "tier_upgrade_required" and upgrade_required: true.

Getting redemption instructions

When a user clicks "Claim", fetch their redemption details. As a side effect the endpoint records an instructions_viewed event and auto-emits a deal_redeemed event for that user — once per (partner, user, deal), so you don't track redemptions on the API path yourself.

curl "https://perks.founderpass.com/v1/deals/{id}/instructions?user_id={user_uuid}" \
-H "Authorization: Bearer $TOKEN"

user_id is required as a query parameter — the server checks that user's tier against the deal's tier and returns 403 with reason: "tier_upgrade_required" and upgrade_required: true if they don't qualify.

:::info Status codes to handle

  • 403 { "reason": "tier_upgrade_required", "upgrade_required": true } — the user's tier is below the deal's.
  • 404 — the deal isn't redeemable for this user. The body tells you why for the gates you can act on:
    • { "reason": "region_restricted" } — the member's regions don't satisfy the deal's region targeting: either they include a region the deal excludes, or they're absent from the deal's include-region list. Sync the member's actual region.
    • { "reason": "user_type_restricted" } — the deal is limited to certain user types the member isn't in.
    • { "reason": "eligibility_mismatch", "dimension": "eligibility_company_stage" } — the member's eligibility doesn't overlap the deal on the named dimension. Set that dimension on the member.
    • No reason (bare { "error": ... }) — the deal isn't available to your account (hidden, api_excluded, or not in your catalogue); nothing to fix at the member level.
  • 409 { "deal_status": "coming_soon" } — the deal is announced but not yet redeemable; its redemption assets are withheld. Treat this as "skip/flag", not an error to retry. (GET /v1/deals already carries each deal's status, so you can filter coming_soon out before reaching this call.) :::

:::tip Pre-filter on region with available_regions List and detail responses include available_regions — the regions a deal is redeemable in, derived from the deal's region restrictions, so you can filter deals to a member's region before attempting redemption rather than discovering a region_restricted 404. It has three states: omitted = available everywhere; a non-empty list = redeemable only in those regions; an empty list [] = the region rules resolve to no regions — don't treat [] as "everywhere". Note this is distinct from eligibility_geography (an eligibility dimension using ISO-3166 country codes); available_regions uses named regions and reflects the deal's geo visibility. :::

:::tip Hide un-claimable deals with claimable_without_eligibility List and detail responses also include claimable_without_eligibility (boolean). It's true when a default member — one you create with just an email — can redeem the deal right now with nothing extra supplied, and false when a default member can't: blocked by a per-member gate (a targeted region, or a non-default eligibility dimension such as eligibility_company_stage or eligibility_funding), or the deal isn't redeemable yet (coming_soon). Filter or flag on it instead of maintaining your own list of un-claimable deals. It's partner-relative (region default = your account's regions) and excludes tier (a premium-only deal is a separate tier_upgrade_required gate). A default member carries no user_types, so user-type-only gates don't make a deal un-claimable. :::

Response

{
"instructions": "<ul><li>Visit the URL</li><li>Enter the promo code</li></ul>",
"promo_code": "FOUNDER20",
"redeem_url": "https://vendor.com/redeem",
"contact_email": "support@vendor.com"
}

:::tip Cache the listing per query, not per user The listing is partner-scoped, so the same (tier, category, page) tuple produces the same response regardless of which member you're serving. Cache by tuple, not by user_id, to keep cardinality manageable. Redemption instructions, on the other hand, are per-user — don't cache those. :::

See the full filter set and response schema in the Deals API Reference.