Extended API Integration

Kard’s Extended API feature enhances our existing rewards endpoints with dynamic, per-user offer data. By requesting UI components through the supportedComponents query parameter, you can surface richer offer experiences to your end users — dynamic descriptions, call-to-action buttons, contextual tags, progress bars, boosted rewards, and logo decorations — without building custom logic to interpret raw offer data.

This guide covers how to request and render extended offer fields from the Get Offers by User and Get Locations by User endpoints.

Key Concepts

Supported Components

The Extended API uses a component-based model to deliver dynamic offer data. You tell Kard which UI components your app supports, and the API returns the relevant data for each offer. Kard evaluates each offer independently and returns only the components that apply to that offer and that you requested.

ComponentTypeDescriptionExample Use Case
shortDescriptionstringA brief, dynamic description of the offer suitable for list/browse views”Multi-use”, “2 of 3 purchases”
longDescriptionstringA detailed description suitable for offer detail pages”Make a qualifying purchase to advance your progress bar toward your next reward.”
baseRewardstringA formatted reward string ready for display"5% cash back"
boostedRewardstringA formatted reward string for the elevated reward after a boost, shown alongside baseReward"10% cash back"
ctaobjectA call-to-action button with text, style, optional start icon, and an optional action URL”Tap to activate”, “Get 10%“
tagsstring[]Contextual labels for the offer in list/browse views["New"], ["Limited supply"]
detailTagsstring[]Contextual labels for the offer detail page["Activated"], ["Boosted 10%"]
logoFlareobjectA colored border and optional badge to decorate the offer logoBolt badge on a boosted offer
progressBarobjectA data-driven progress bar for redemption counts, punch cards, and tiered/progressive rewards”2 of 3” punch card, redemption tracker

How Components Are Returned

The components included in the response depend on what you request via the supportedComponents query parameter and on each offer’s configuration.

Support as many components as possible. Kard renders each offer with the richest representation its state can take from the components you support, so the more you support, the more your users can see and act on. When an offer relies on a component you did not request — a cta to activate, a progressBar for a punch card, a boostedReward for a boost — Kard falls back to expressing that state through shortDescription and longDescription. If you don’t support those either, the state is folded into the offer’s name (for example, "World's Greatest Chicken - tap to activate").

  • All components (shortDescription, longDescription, baseReward, boostedReward, cta, tags, detailTags, logoFlare, progressBar): The full experience. Provides dynamic descriptions, formatted rewards, call-to-action buttons, contextual tags, logo decorations, and progress bars for both list and detail views. Best for apps that support interactive and visual elements.
  • Description-only components (shortDescription, longDescription): Provides dynamic descriptions without interactive elements. Best for apps that cannot render buttons, tags, or progress bars. In this case, Kard uses a VIEW event on the offer as the activation signal. When a user views an offer detail page, you fire a VIEW attribution event. After the event, re-fetch the offer to display the updated descriptions reflecting the new user state.

At minimum, support shortDescription and longDescription. They are the universal fallback for offer state: when an offer requires a richer component your app doesn’t support (for example a cta or a progressBar), its state is expressed through these descriptions instead. Without them, that state is only available in a modified offer name — and your users may miss required actions like activating or boosting an offer.

Which components an offer surfaces depends on its configuration. For example:

  • A standard activatable offer surfaces a cta (and tags/detailTags) until it is activated.
  • A boostable offer surfaces a boost cta before boosting, then a boostedReward (with baseReward struck through) and a logoFlare decoration after boosting.
  • A multi-redemption, punch-card, or progressive-reward offer surfaces a progressBar (plus a matching shortDescription).
  • A limited-supply offer surfaces a "Limited supply" tag and remaining-redemption copy in its descriptions.

You can request any combination of components. Kard evaluates each offer independently and returns only the components that apply to that offer. If a component does not apply, it is simply omitted — always check for presence before rendering.

Implementation

Step 1: Add the supportedComponents Query Parameter

Append the supportedComponents query parameter to your existing Get Offers by User or Get Locations by User requests. Use repeated parameters for multiple component types.

Get Offers by User with all components:

GET /v2/issuers/{{organizationId}}/users/{{userId}}/offers?supportedComponents=shortDescription&supportedComponents=longDescription&supportedComponents=baseReward&supportedComponents=boostedReward&supportedComponents=cta&supportedComponents=tags&supportedComponents=detailTags&supportedComponents=logoFlare&supportedComponents=progressBar

Get Locations by User with all components:

GET /v2/issuers/{{organizationId}}/users/{{userId}}/locations?include=offers&supportedComponents=shortDescription&supportedComponents=longDescription&supportedComponents=baseReward&supportedComponents=boostedReward&supportedComponents=cta&supportedComponents=tags&supportedComponents=detailTags&supportedComponents=logoFlare&supportedComponents=progressBar

Get Offers by User with description-only components:

GET /v2/issuers/{{organizationId}}/users/{{userId}}/offers?supportedComponents=shortDescription&supportedComponents=longDescription

For Get Locations by User, you must include include=offers in the query to receive offer data with components.

supportedComponents accepts only the values listed in the Supported Components table. Requesting an unknown value returns a 400 Invalid Request.

Step 2: Parse the components Object

When supportedComponents is provided and an offer is eligible for extended data, the response includes a components object within the offer attributes.

Example Response with all components (activatable offer, not yet activated):

1{
2 "data": [
3 {
4 "type": "standardOffer",
5 "id": "5e27318c9b346f00087fbb5c",
6 "attributes": {
7 "name": "Worlds Greatest Chicken",
8 "userReward": {
9 "type": "PERCENT",
10 "value": 5.7
11 },
12 "purchaseChannel": ["INSTORE"],
13 "startDate": "2024-11-17T05:00:00Z",
14 "expirationDate": "2025-03-17T05:00:00Z",
15 "terms": "Worlds Greatest Chicken offers are only available within US Locations.",
16 "isTargeted": true,
17 "assets": [
18 {
19 "url": "https://attribution.getkard.com/logos/wgc_logo.png?token=example",
20 "alt": "",
21 "type": "IMG_VIEW"
22 }
23 ],
24 "components": {
25 "shortDescription": "Multi-use",
26 "longDescription": "Use your linked card on qualifying purchases to redeem multiple times.",
27 "baseReward": "5.7% cash back",
28 "cta": {
29 "buttonText": "Activate",
30 "buttonStyle": "PRIMARY",
31 "action": {
32 "url": "/v2/issuers/{{organizationId}}/users/{{userId}}/offers/5e27318c9b346f00087fbb5c/activate?include=offer&supportedComponents=cta&supportedComponents=tags&supportedComponents=detailTags",
33 "method": "POST"
34 }
35 },
36 "tags": [],
37 "detailTags": ["Activated"]
38 }
39 },
40 "relationships": {
41 "category": {
42 "data": [
43 { "type": "category", "id": "65920081b524d126068de24a" }
44 ]
45 }
46 }
47 }
48 ],
49 "links": {
50 "self": "/v2/issuers/{{organizationId}}/users/{{userId}}/offers?supportedComponents=cta&supportedComponents=tags&supportedComponents=detailTags",
51 "prev": null,
52 "next": null
53 },
54 "included": [
55 {
56 "type": "category",
57 "id": "65920081b524d126068de24a",
58 "attributes": { "name": "Food & Beverage" }
59 }
60 ]
61}

Example components for a boostable offer (after the user has boosted it):

1"components": {
2 "shortDescription": "Boosted 10%",
3 "longDescription": "Boosted 10%. Earn 10% cash back at Worlds Greatest Chicken with your eligible card.",
4 "baseReward": "5% cash back",
5 "boostedReward": "10% cash back",
6 "tags": [],
7 "detailTags": ["Boosted 10%"],
8 "logoFlare": {
9 "borderColor": "PRIMARY",
10 "badge": {
11 "icon": "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\"><path fill=\"currentColor\" d=\"M13 2 4 14h6l-1 8 9-12h-6z\"/></svg>",
12 "position": "TOP_RIGHT"
13 }
14 }
15}

Before the offer is boosted, the same offer instead returns a boost cta (a SECONDARY button such as “Get 10%”) and no boostedReward/logoFlare. See Handle Call-To-Action Events.

Example components for a punch-card offer (rewards every 3rd purchase; user is 2 of 3):

1"components": {
2 "shortDescription": "Make 3 purchases to earn",
3 "progressBar": {
4 "total": 3,
5 "currentProgress": 2,
6 "labels": {
7 "default": { "left": "", "right": "2 of 3" }
8 },
9 "segments": {
10 "default": { "position": "LEFT", "selection": "CURRENT_AND_BELOW" },
11 "progress": [
12 { "completed": 1, "total": 1 },
13 { "completed": 1, "total": 1 },
14 { "completed": 0, "total": 1 }
15 ]
16 }
17 }
18}

See Rendering the Progress Bar for the full model and more examples.

Step 3: Render Components in Your UI

How you render the components depends on which ones you requested and which the offer returns. Always check for presence before rendering — an offer that isn’t eligible for a component simply omits it.

Descriptions and rewards

  1. Display shortDescription on offer cards in list/browse views.
  2. Display longDescription on the offer detail page.
  3. Display baseReward as the formatted reward amount (e.g., “5.7% cash back”).
  4. If boostedReward is present, render it as the active reward and show baseReward with a strikethrough to communicate the uplift (e.g., 5% cash back 10% cash back).

Call-to-action button (cta)

  1. If cta is present, display the call-to-action button using buttonText:
    • Style the button per buttonStyle: PRIMARY (prominent, e.g. activation), SECONDARY (lower-emphasis, e.g. boost), or DISABLED (rendered but not interactive).
    • If startIcon is present, render it (an inline SVG string) before the button text.
    • When tapped, call the URL in cta.action.url using cta.action.method (see Step 4a).
  2. If cta is absent, the offer requires no action right now (e.g. it has already been activated) — hide the button.

Tags

  1. Render tags as overlays or badges on the offer card in list/browse views (may be empty).
  2. Render detailTags as labels on the offer detail page.

Logo flare (logoFlare)

  1. If logoFlare is present, decorate the offer’s logo/thumbnail:
    • Draw a border around the logo using borderColor (PRIMARY or SECONDARY, mapped to your theme colors).
    • If badge is present, overlay the badge.icon (an inline SVG) at the corner given by badge.position.

Progress bar (progressBar)

  1. If progressBar is present, render it per Rendering the Progress Bar. Use the compact default layout on cards and the richer details layout on the offer detail page.

All components, offer not activated:

All components, offer not activated

All components, offer is activated:

All components, offer activated

If you requested description-only components (shortDescription and longDescription):

This approach is for issuers that cannot render interactive call-to-action buttons or graphical elements. Instead of using a call-to-action button to activate or boost offers, the VIEW event serves as the activation signal. Progress and state are still communicated through the descriptions (e.g. shortDescription reads “2 of 3 purchases” for a punch-card offer).

  1. Display shortDescription on offer cards in list/browse views.
  2. Display longDescription on the offer detail page.
  3. When a user navigates to the offer detail page, fire a VIEW attribution event (see Step 4b).
  4. After the VIEW event, re-fetch the offer to get updated descriptions that reflect the new user state.
  5. Re-render the offer with the updated shortDescription and longDescription.

Important: Without a call-to-action button, the user has no explicit activation action. The VIEW event acts as a proxy for activation. You must re-fetch and re-render the offer after firing the VIEW event to display the updated state to the user.

Step 4a: Handle Call-To-Action Events

When a user taps a call-to-action button, call the endpoint provided in cta.action using the given method. Use cta.action.url and cta.action.method exactly as provided — the path, query string, and method are pre-configured by Kard. Different offers point at different actions:

  • Activation offers point at the activate endpoint:

    POST /v2/issuers/{{organizationId}}/users/{{userId}}/offers/{{offerId}}/activate
  • Boost offers point at the boost endpoint:

    POST /v2/issuers/{{organizationId}}/users/{{userId}}/offers/{{offerId}}/boost

The cta.action.url Kard returns already includes include=offer and your requested supportedComponents, so the response contains the updated offer:

POST /v2/issuers/{{organizationId}}/users/{{userId}}/offers/{{offerId}}/activate?include=offer&supportedComponents=shortDescription&supportedComponents=longDescription&supportedComponents=cta&supportedComponents=tags&supportedComponents=detailTags

Activation response (201 Created):

1{
2 "data": {
3 "type": "offerAttribution",
4 "id": "attribution-event-id",
5 "attributes": {
6 "entityId": "5e27318c9b346f00087fbb5c",
7 "eventCode": "ACTIVATE",
8 "medium": "CTA",
9 "eventDate": "2025-01-07T16:30:00Z"
10 }
11 },
12 "included": [
13 {
14 "type": "standardOffer",
15 "id": "5e27318c9b346f00087fbb5c",
16 "attributes": {
17 "name": "Worlds Greatest Chicken",
18 "userReward": { "type": "PERCENT", "value": 5.7 },
19 "components": {
20 "shortDescription": "Multi-use",
21 "longDescription": "Use your linked card on qualifying purchases to redeem multiple times.",
22 "tags": [],
23 "detailTags": ["Activated"],
24 "baseReward": "5.7% cash back"
25 }
26 }
27 }
28 ]
29}

Boost response (201 Created): identical in shape, but eventCode is BOOST. The included offer now carries the elevated boostedReward, the struck-through baseReward, and a logoFlare:

1{
2 "data": {
3 "type": "offerAttribution",
4 "id": "attribution-event-id",
5 "attributes": {
6 "entityId": "5e27318c9b346f00087fbb5c",
7 "eventCode": "BOOST",
8 "medium": "CTA",
9 "eventDate": "2025-01-07T16:30:00Z"
10 }
11 },
12 "included": [
13 {
14 "type": "standardOffer",
15 "id": "5e27318c9b346f00087fbb5c",
16 "attributes": {
17 "name": "Worlds Greatest Chicken",
18 "components": {
19 "baseReward": "5% cash back",
20 "boostedReward": "10% cash back",
21 "tags": [],
22 "detailTags": ["Boosted 10%"],
23 "logoFlare": {
24 "borderColor": "PRIMARY",
25 "badge": {
26 "icon": "<svg viewBox=\"0 0 24 24\" width=\"24\" height=\"24\"><path fill=\"currentColor\" d=\"M13 2 4 14h6l-1 8 9-12h-6z\"/></svg>",
27 "position": "TOP_RIGHT"
28 }
29 }
30 }
31 }
32 }
33 ]
34}

Use the updated offer from included to immediately refresh your UI without re-fetching the full offer list. After a successful action the offer’s components change (for example, an activation drops the cta and sets detailTags to ["Activated"]; a boost drops the boost cta and adds boostedReward + logoFlare).

Step 4b: Handle VIEW Event Activation (Description-Only)

If you are only using shortDescription and longDescription (no cta), use the VIEW attribution event to signal that a user has engaged with an offer. This acts as the activation trigger.

When a user opens an offer detail page, fire a VIEW event:

POST /v2/issuers/{{organizationId}}/users/{{userId}}/offers/{{offerId}}/view

After a successful VIEW event, re-fetch the offer with your supported components to get the updated descriptions:

GET /v2/issuers/{{organizationId}}/users/{{userId}}/offers?supportedComponents=shortDescription&supportedComponents=longDescription

The descriptions will now reflect the updated user state:

1"components": {
2 "shortDescription": "Activated",
3 "longDescription": "Activated"
4}

Re-render the offer detail page with the updated longDescription, and update the offer card with the new shortDescription in your list views.

We recommend integrating with call-to-action components when possible. The call-to-action flow gives users a clear, explicit action to activate and boost offers and provides a better user experience than the implicit VIEW event approach.

Rendering the Progress Bar

The progressBar component tracks a user’s progress toward a reward. It powers three offer types with a single, fully data-driven model — you do not need to know the offer type to render it correctly:

  • Redemption count — a multi-use offer, tracking how many of N redemptions the user has used.
  • Punch card — an offer that rewards every N qualifying purchases, tracking progress toward the next reward.
  • Progressive / tiered rewards — an offer whose reward grows as the user climbs tiers.

Anatomy

1"progressBar": {
2 "total": 3,
3 "currentProgress": 1,
4 "labels": {
5 "default": { "left": "", "right": "2 of 3" },
6 "details": { "left": "Redemptions", "right": "" }
7 },
8 "segments": {
9 "default": { "position": "LEFT", "selection": "CURRENT_AND_BELOW", "icon": "<svg…/>", "separator": "LINE", "labels": [ { "title": "5%", "description": "" } ] },
10 "details": { "position": "RIGHT", "selection": "CURRENT" },
11 "progress": [
12 { "completed": 1, "total": 1 },
13 { "completed": 0, "total": 1 },
14 { "completed": 0, "total": 1 }
15 ]
16 }
17}
  • total — the number of nodes/steps in the bar.
  • currentProgress — how far the user has progressed (clamp to 0..total when rendering).
  • labels — the left/right text that flanks the bar, per layout (see below).
  • segments — how to draw the bar, per layout, plus the per-node progress fill array. Absent for a plain continuous bar.

Pick the layout for your surface

A single progressBar payload carries two layouts so you can render the same data differently on a card versus a detail page:

  • default — the compact layout for list/browse/card views. Use labels.default and segments.default.
  • details — the richer layout for the offer detail page. Use labels.details and segments.details, each falling back to default when not present.

Choose the visual style

The resolved segment configuration (segments.default or segments.details) determines the visual style:

  1. Continuous bar — when segments is absent. Draw a single bar filled to currentProgress / total, flanked by the left/right labels. (Used for redemption-count offers with more than 5 redemptions.)
  2. Segmented bars — when segments is present with no separator. Draw one bar per node (total nodes). Fill each node i to progress[i].completed / progress[i].total. Nodes support partial fill (e.g. 2 / 4), which a punch card uses to show progress within the current step. If a segment provides per-node labels, render each label next to its bar.
  3. Icon tiers with line separators — when segments.<layout>.separator is LINE and position is FULL_WIDTH. Draw the icon (an inline SVG) for each node, connected by horizontal lines, with the per-node labels[i].title beneath. Icons are binary (active/inactive) and ignore partial fill. (Used for progressive/tiered rewards.)

Fill and selection rules

  • Per-node fill comes from segments.progress[i] (completed / total). When progress is absent, a node is either full or empty based on selection.
  • selection decides which nodes read as “reached”:
    • CURRENT — nodes with index < currentProgress.
    • CURRENT_AND_BELOW — nodes with index <= currentProgress.
  • icon SVGs are themeable via the CSS custom properties --icon-fill, --icon-outline, and --icon-detail. Set these on the icon’s container to match your theme’s active/inactive colors; icons that don’t reference the variables fall back to their embedded colors.
  • position places the segments relative to the labels: LEFT, RIGHT, or FULL_WIDTH (segments span the full width with labels above).

Example: redemption-count bar

A multi-use offer redeemable up to 3 times, where the user has redeemed 1. Five or fewer redemptions render as segmented nodes with a check icon; more than five render as a continuous bar.

1"shortDescription": "Use up to 3 times",
2"progressBar": {
3 "total": 3,
4 "currentProgress": 1,
5 "labels": {
6 "default": { "left": "", "right": "" },
7 "details": { "left": "Redemptions", "right": "" }
8 },
9 "segments": {
10 "default": { "icon": "<svg…check…/>", "position": "LEFT", "selection": "CURRENT" },
11 "details": { "icon": "<svg…check…/>", "position": "RIGHT", "selection": "CURRENT" },
12 "progress": [
13 { "completed": 1, "total": 1 },
14 { "completed": 0, "total": 1 },
15 { "completed": 0, "total": 1 }
16 ]
17 }
18}

Renders as three check icons, the first filled. On the detail page a “Redemptions” label sits to the left.

Redemption-count progress bar: three check icons with the first filled

Example: punch card

An offer that rewards every 3rd qualifying purchase, with the user 2 of 3 into the current card:

1"shortDescription": "Make 3 purchases to earn",
2"progressBar": {
3 "total": 3,
4 "currentProgress": 2,
5 "labels": {
6 "default": { "left": "", "right": "2 of 3" }
7 },
8 "segments": {
9 "default": { "position": "LEFT", "selection": "CURRENT_AND_BELOW" },
10 "progress": [
11 { "completed": 1, "total": 1 },
12 { "completed": 1, "total": 1 },
13 { "completed": 0, "total": 1 }
14 ]
15 }
16}

Renders as three bars, the first two filled and the third empty, with a “2 of 3” label to the right. Once the user redeems every reward the offer allows, every node reads full and the copy switches to a “reward redeemed” message.

Punch-card progress bar: three bars with the first two filled and a 2 of 3 label

Example: progressive / tiered rewards

An offer with three reward tiers (5%, 10%, 15%) where the user has reached the first tier:

1"shortDescription": "Earn more with each purchase",
2"baseReward": "10% cash back",
3"progressBar": {
4 "total": 3,
5 "currentProgress": 1,
6 "labels": { "default": { "left": "", "right": "" } },
7 "segments": {
8 "default": {
9 "position": "FULL_WIDTH",
10 "separator": "LINE",
11 "icon": "<svg…ring…/>",
12 "labels": [
13 { "title": "5%", "description": "" },
14 { "title": "10%", "description": "" },
15 { "title": "15%", "description": "" }
16 ],
17 "selection": "CURRENT_AND_BELOW"
18 },
19 "progress": [
20 { "completed": 1, "total": 1 },
21 { "completed": 0, "total": 1 },
22 { "completed": 0, "total": 1 }
23 ]
24 }
25}

Renders as three ring icons joined by lines with “5%”, “10%”, “15%” beneath — the reached tiers highlighted. When a progressive offer is also a punch card, Kard omits the icon/separator so the raw segmented bars can show partial fill toward the next tier.

Progressive rewards progress bar: three ring tiers labeled 5%, 10%, 15% joined by lines

Rendering algorithm

  1. Pick the layout: details on the detail page, otherwise default.
  2. Resolve labels = labels[layout] ?? labels.default and segment = segments[layout] ?? segments.default.
  3. If there is no segments object, draw a continuous bar filled to currentProgress / total between the left/right labels. Done.
  4. Otherwise, for each node i in 0..total-1, compute its fill: progress[i].completed / progress[i].total if provided, else full/empty per selection.
  5. If segment.separator is LINE (and position is FULL_WIDTH), draw icons joined by lines with labels[i].title beneath (icons are binary). Otherwise draw one bar per node, applying each node’s fractional fill and any per-node label.
  6. Place the label block according to position (LEFT, RIGHT, or FULL_WIDTH).

Progress is display-only and dynamic. Progress bar values reflect the user’s current state at request time and change as the user transacts. Do not cache them across sessions; re-fetch to get the latest state.

Component Reference

components Object

FieldTypeDescription
shortDescriptionstring (optional)Brief dynamic description for list/browse views
longDescriptionstring (optional)Detailed dynamic description for offer detail pages
baseRewardstring (optional)Formatted reward string for display (e.g. “5.7% cash back” or “$2.00 cash back”)
boostedRewardstring (optional)Formatted elevated reward string shown after a boost; render as the active reward with baseReward struck through
ctaobject (optional)Call-to-action button configuration
tagsstring[] (optional)Contextual labels for list/browse views
detailTagsstring[] (optional)Contextual labels for offer detail pages
logoFlareobject (optional)Logo border + badge decoration
progressBarobject (optional)Progress bar for redemption counts, punch cards, and tiered rewards

cta Object

FieldTypeDescription
buttonTextstringText to display on the button (e.g., “Activate”, “Get 10%“)
buttonStyleenumPRIMARY (prominent, e.g. activate), SECONDARY (lower-emphasis, e.g. boost), or DISABLED (rendered but non-interactive). The cta is omitted entirely once the action has been taken (e.g. an activated offer).
startIconstring (optional)Inline SVG string to render before the button text
actionobject (optional)Contains the URL and method to call when the button is tapped
action.urlstringAPI endpoint to call when the button is tapped (already includes include=offer and your supportedComponents)
action.methodstringHTTP method to use (e.g., POST)

logoFlare Object

FieldTypeDescription
borderColorenumBorder color around the logo: PRIMARY or SECONDARY (map to your theme colors)
badgeobject (optional)Corner badge overlay
badge.iconstringInline SVG string for the badge icon
badge.positionenumTOP_RIGHT, TOP_LEFT, BOTTOM_RIGHT, or BOTTOM_LEFT

progressBar Object

FieldTypeDescription
totalintegerNumber of nodes/steps in the bar
currentProgressintegerHow far the user has progressed (clamp to 0..total)
labelsobjectLeft/right labels per layout (default, details)
segmentsobject (optional)Segment configuration per layout, plus the per-node progress fill array. Absent for a continuous bar.
labelstring (deprecated)Legacy single label. Use labels instead.
segmentedboolean (deprecated)Legacy flag. Rely on the presence of segments instead.

progressBar.labels

FieldTypeDescription
defaultobjectLabel pair for the default (card) layout
detailsobject (optional)Label pair for the detail-page layout (falls back to default)
default.left / default.rightstring (optional)Text for the left/right label

progressBar.segments

FieldTypeDescription
defaultobjectSegment configuration for the default (card) layout
detailsobject (optional)Segment configuration for the detail-page layout (falls back to default)
progressobject[]Per-node fill state, index-aligned with the nodes (length equals total)

progressBar.segments.default / .details (segment configuration)

FieldTypeDescription
positionenumLEFT, RIGHT, or FULL_WIDTH — placement of the segments relative to labels
selectionenum (optional)Which nodes read as reached: CURRENT (index < currentProgress) or CURRENT_AND_BELOW (index <= currentProgress)
separatorenum (optional)LINE renders icon nodes joined by horizontal lines (requires position: FULL_WIDTH)
iconstring (optional)Inline SVG for each node; themeable via --icon-fill, --icon-outline, --icon-detail
labelsobject[] (optional)Per-node labels (title, description)

progressBar.segments.progress[]

FieldTypeDescription
completedintegerUnits completed within this node
totalintegerUnits required to complete this node (a node fills to completed / total)

Important Notes

Component availability is per-offer. Different offers in the same response may return different components depending on their configuration.

The components object is additive. Existing fields like name, userReward, terms, and assets are always returned regardless of whether you request components.

Component values are dynamic. Descriptions, tags, CTA state, boosted rewards, and progress bars reflect the user’s state at request time and can change between requests. Re-fetch to get the latest state; do not cache component data across sessions.

Best Practices

  • Support as many components as possible. Different offers rely on different components (activation, boosts, punch cards, tiered rewards, limited supply). The more you support, the more offers render fully; unsupported features degrade to shortDescription/longDescription, and then to a modified offer name.
  • At minimum, support shortDescription and longDescription. They are the universal fallback for any offer state you don’t otherwise support — without them, that state is only available in the offer name and users may miss required actions.
  • Always check for the presence of a component before rendering. Not every offer includes every component — your UI should gracefully fall back to standard offer fields.
  • Use cta.action exactly as provided. The URL (including its query string) and method are pre-configured by Kard. Do not modify them.
  • Render state changes immediately. After a successful cta action, use the updated offer from the response included array to update your UI without waiting for a full list refresh.
  • Choose components based on your UI capabilities. If you cannot render interactive buttons or graphical elements, request shortDescription and longDescription — progress and state are still conveyed through the descriptions.
  • Request only the components you support. Avoid requesting components you don’t intend to render.
  • Show the reward uplift for boosts. When boostedReward is present, render it as the active reward and strike through baseReward so the uplift is clear.
  • Render the progress bar per surface. Use the default layout on cards and the details layout on the offer detail page, and honor per-node progress fill so punch cards show partial progress.
  • Handle the absence of cta after an action. When an offer has been activated or boosted, the cta is omitted. Hide the button when cta is absent.
  • Do not cache component data across sessions. Values like tags, CTA state, boosted rewards, and progress are dynamic and may change between requests.