Docs
Get in touchPartner Portal

Data models

At-a-glance shape of the main objects. The API Reference generated from swagger-partner.json is the authoritative source, this page is the summary you can scan in one sitting.

Deal

{
  "id": "uuid",
  "product_name": "string",
  "slug": "string",
  "logo_url": "string",
  "product_url": "string",
  "short_description": "string",
  "long_description": "string",
  "perk_description": "string | null",
  "market_value": "string",
  "est_value": "number | null",
  "categories": "<Category>[]",
  "deal_type": "founderpass | partner_private",
  "owner_partner_id": "uuid | null",
  "tier": "free | premium",
  "tags": "DealTag[]",
  "badges": "string[]",
  "redemption": "<Redemption> | null",
  "inquiry_form_fields": "InquiryFormField[] (form-redemption deals; [] otherwise)",
  "eligibility_requirements": "string | null",
  "eligibility_customer_type": "string[]",
  "eligibility_company_stage": "string[]",
  "eligibility_company_size": "string[]",
  "eligibility_business_type": "string[]",
  "eligibility_geography": "string[]",
  "eligibility_incorporation": "string[]",
  "eligibility_funding": "string[]",
  "offer_types": "string[]",
  "affiliate_network": "partnerstack | cj | impact | null",
  "approval_required": false,
  "approval_time": "string | null",
  "status": "draft | live | expired | paused | coming_soon",
  "start_date": "ISO 8601 date | null",
  "end_date": "ISO 8601 date | null",
  "active": true,
  "hidden_globally": false,
  "api_excluded": false,
  "available_regions": "AudienceRegion[] (omitted = all regions; [] = no regions)",
  "claimable_without_eligibility": "boolean (true = a default member can redeem with nothing extra supplied)",
  "created_at": "ISO 8601",
  "updated_at": "ISO 8601"
}

available_regions is the named-region allow-list where the deal is redeemable, derived from its region visibility rules. Three states: omitted = available in all regions; a non-empty list = redeemable only in those regions; an empty list [] = the region rules resolve to no regions (don’t read [] as “all”). It is distinct from eligibility_geography (an eligibility dimension, ISO-3166 country codes), see Listing deals.

claimable_without_eligibility is true when a default member (created with just an email) can redeem the deal right now with nothing extra supplied; false when they can’t, blocked by a per-member gate (a targeted region, or a non-default eligibility dimension) or not redeemable yet (coming_soon). Partner-relative (region default = your account’s regions) and excludes tier gating; user-type-only gates don’t count (a default member has no user_types). Use it to hide un-claimable deals, see Listing deals.

See DealTag and DealStatus for valid enum values, and Listing deals for the eligibility-array vocabularies. api_excluded is a super_admin-managed flag that hides a deal from the Partner API channel while keeping it on the hosted app, see Visibility rules.

Partner-private deals created with POST /v1/partner-deals start as draft by default. Draft deals are returned by the partner-deal management endpoints, but they are not visible in GET /v1/deals until you publish them with PATCH /v1/partner-deals/{id} and status: "live".

inquiry_form_fields is the request-form schema for form-redemption deals (redemption.method = form); it is [] for every other deal. Each element is { "key": "string", "label": "string", "type": "text | textarea | email | number | select", "required": true, "options": "string[] (select only)" }. affiliate_network is populated only on affiliate deals whose redeem URL is wrapped; null otherwise.

Redemption

Embedded inside Deal.redemption. For the partner-specific instructions a member sees when they claim, hit GET /v1/deals/{id}/instructions?user_id={user_id}, same shape, but personalized.

{
  "method": "url | promo_code | steps | unique_code | form",
  "instructions": "string (HTML)",
  "promo_code": "string",
  "redeem_url": "string",
  "contact_email": "string"
}

User

What POST /v1/users returns and what GET /v1/users lists.

{
  "id": "uuid",
  "partner_id": "uuid | null",
  "email": "string",
  "external_user_id": "string | null",
  "firebase_uid": "string | null",
  "role": "super_admin | partner_admin | user",
  "tier": "free | premium",
  "user_types": "UserType[]",
  "regions": "AudienceRegion[]",
  "eligibility_customer_type": "string[]",
  "eligibility_company_stage": "string[]",
  "eligibility_company_size": "string[]",
  "eligibility_business_type": "string[]",
  "eligibility_geography": "string[]",
  "eligibility_incorporation": "string[]",
  "eligibility_funding": "string[]",
  "created_at": "ISO 8601",
  "updated_at": "ISO 8601"
}

The POST /v1/users response also includes sign_in_url (magic link) and already_exists (boolean), see the Syncing users guide.

See UserType and AudienceRegion for valid enum values. The flat eligibility_* arrays mirror a deal’s eligibility dimensions and gate which gated deals the user can see/redeem. They are set via a nested eligibility object on POST/PATCH /v1/users but returned flat (as above), using the same vocabularies as Listing deals → eligibility; only customer_type / company_size / incorporation resolve to permissive defaults when omitted.

VisibilityRule

{
  "id": "uuid",
  "deal_id": "uuid",
  "rule_type": "include_partner | exclude_partner | include_partner_type | exclude_partner_type | include_region | exclude_region | include_user_type | exclude_user_type",
  "rule_value": ["string"],
  "priority": 0,
  "created_at": "ISO 8601",
  "updated_at": "ISO 8601"
}

See Visibility rules for what each rule_type controls and RuleType for the full enum.

Analytics summary

What GET /v1/analytics?days=N returns.

{
  "total_users": 0,
  "active_users": 0,
  "total_deal_views": 0,
  "total_redemptions": 0,
  "upgrade_cta_clicks": 0,
  "top_deals": [
    { "deal_id": "uuid", "product_name": "string", "view_count": 0, "click_count": 0, "redemption_count": 0 }
  ],
  "event_counts": {
    "deal_list_viewed": 0,
    "deal_detail_viewed": 0,
    "deal_redeemed": 0
  }
}

event_counts is a map keyed by event type, holding a count for every event type recorded in the window (the three keys above are illustrative, not the full set). total_deal_views, total_redemptions, and upgrade_cta_clicks are the deal_detail_viewed, deal_redeemed, and upgrade_cta_clicked counts pulled out of that map.