Skip to content

Core concepts

Read this once and the rest of the API stops surprising you.


classDiagram direction LR class Store { +string id +string store_name +string currency +string time_zone +bool stamps_enabled +string privacy_policy_url } class Department { +string id +string name } class Member { +string id +Phone phone +string first_name +string last_name +string email +Gender gender +MemberConsent consent +string[] department_ids } class Segment { +int id +string text +SegmentType type +bool is_required +SegmentOption[] options } class Coupon { +string code +string title +CouponType type +DiscountType discount_type +decimal discount_value +int max_redemptions } class StampCard { +int id +string title +int required_stamps +string card_type +decimal threshold_amount } class Transaction { +string external_id +datetime created_at +decimal total +string currency +Detail[] details } Store "1" --> "*" Department Store "1" --> "*" Member Store "1" --> "*" Segment Store "1" --> "*" Coupon Store "1" --> "*" StampCard Member "1" --> "*" Transaction Member "1" --> "*" Coupon : holds Member "1" --> "*" StampCard : collects Member "*" --> "*" Department : belongs to Member "*" --> "1" Department : signed up at Transaction "*" --> "1" Department : originated at

IdLooks likeNotes
Store idAb3xK_9pQrLmRead it from GET /stores; it matches your client id. Case-sensitive.
Member idm_nsr714RRBEHXOpaque. Case-sensitive. Store it against your customer record.
Department id101A string in JSON even when it looks numeric
Coupon codeWELCOME15Member coupon endpoints address coupons by code, never by numeric id
Transaction idORDER-1001Yours. You choose it; we store it as external_id

Treat store and member ids as opaque strings. Never lowercase, trim, or numerically parse them.


A department is where an action came from. Discover them once and cache the result:

GET /api/v2.0/stores/{storeId}/departments
[
{ "id": "101", "name": "Web" },
{ "id": "102", "name": "Oslo" }
]

Most webshops use a single department for all online orders. If the merchant wants online and in-store reported separately — and they usually do — this is the mechanism.

department_id appears in four places, and they are not redundant:

WhereMeaning
origin.department_idWhere this API call came from. On a signup call, that is the department the member was created in
department_id on enrollRequired. The department an enrolled member is associated with
Transaction department_idWhich department the sale belongs to
Member department_ids[]Which departments the member is associated with

Only department_ids[] comes back on the member. The origin department is recorded against the call, not echoed on the member, so store it yourself if you need to report on where a member signed up.


Segments — the store’s own profile questions

Section titled “Segments — the store’s own profile questions”

Segments are custom questions a merchant defines: interests, clothing size, whether the customer is a student. They are how a merchant targets campaigns, so getting them right matters commercially.

GET /api/v2.0/stores/{storeId}/segments
[
{
"id": 633,
"text": "What are you interested in?",
"type": "Checkbox",
"is_visible_to_follower": true,
"is_required": false,
"options": [
{ "id": 4101, "text": "Repairs" },
{ "id": 4102, "text": "Consulting" },
{ "id": 4103, "text": "Project management" }
]
},
{
"id": 2222,
"text": "Student",
"type": "Radio",
"is_visible_to_follower": true,
"is_required": false,
"options": [
{ "id": 5001, "text": "Yes" },
{ "id": 5002, "text": "No" }
]
}
]

Five types: Text, DateTime, Radio, Checkbox, Dropdown.

Render your signup form from this response. Do not hardcode the questions — the merchant can change them in Diller at any time, and a hardcoded form silently stops collecting the new ones.

Answers go in the segments array on member create and update. Which field you fill depends on the type:

Segment typeFillLeave out
Text, DateTimevalueselected_options
Radio, Dropdownselected_options (exactly one)value
Checkboxselected_options (zero or more)value
"segments": [
{ "segment_id": 633, "selected_options": [4101, 4103] },
{ "segment_id": 2222, "selected_options": [5001] },
{ "segment_id": 900, "value": "Regular size M" }
]

selected_options holds option ids, not option labels. Map the label the customer clicked back to its id from the segments response.


Section titled “Consent — four flags, one of them mandatory”
"consent": {
"gdpr_accepted": true,
"save_order_history": true,
"receive_sms": true,
"receive_email": false
}
FlagMeaningRequired?
gdpr_acceptedMember joined the programme and accepted the termsMust be true
save_order_historyMay link purchases to their profileOptional
receive_smsMay send marketing SMSAlways send it — see below
receive_emailMay send marketing emailAlways send it — see below

If you are porting a signup form from a Diller plugin, the mapping is:

Plugin form fieldAPI field
membership_consent_acceptedconsent.gdpr_accepted
purchase_history_consent_acceptedconsent.save_order_history

gdpr_accepted: false is rejected, not stored. Both create and update return 400:

The 'gdpr_accepted' property on the 'Consent' object must be set to true.

On update the message continues: “If your goal is to delete the member, please use the DELETE endpoint.” That is the intended path for a withdrawal of consent — see Members reference.

Your signup form must show the merchant’s own terms and link their privacy policy, available as privacy_policy_url on the store object.


A coupon is a discount the member holds, addressed by code.

{
"id": 0,
"external_ids": [ { "platform": "Shopify", "external_id": "gid://shopify/DiscountCodeNode/1" } ],
"code": "WELCOME15",
"title": "15% off your first order",
"description": "Welcome to the club",
"valid_from": "2026-01-01T00:00:00",
"valid_to": "2026-12-31T23:59:59",
"type": "Welcome",
"discount_type": "Percentage",
"discount_value": 15.0,
"max_redemptions": 1,
"times_redeemed": 0,
"product_ids": ["SKU-123"],
"image_url": null
}
FieldValues
typeStandard, Welcome, Campaign, Public, Recurring, Birthday, ChildBirthday, Automation
discount_typeNotDefined, Percentage, Fixed, FreeShipping

external_ids[] maps a Diller coupon to its counterpart in your platform — useful if you mirror Diller coupons as native discount codes.

max_redemptions is never null. A coupon with no limit carries a large sentinel instead: 1000 on the store’s coupon list, 10000000 on a member’s. Do not show max_redemptions - times_redeemed as “uses left” without allowing for it — an unlimited coupon would read “9,999,999 uses left”. Treat either value as unlimited and show no count.

We do not apply discounts to your cart. We tell you what the member holds and track redemption; your checkout does the arithmetic. See Checkout and coupons.


Collect n stamps, get a reward.

{
"id": 12,
"title": "Coffee card",
"required_stamps": 10,
"stamps_collected": 3,
"card_type": "standard",
"threshold_amount": 0.0,
"is_restartable": true,
"valid_from": "2026-01-01T00:00:00",
"valid_to": "2026-12-31T23:59:59"
}
  • card_type: "standard" — one stamp per qualifying product.
  • card_type: "threshold" — one stamp per order above threshold_amount.

Stamps are awarded through the transaction you post. See Transactions.


Points live on the member:

"membership_detail": {
"points": 250.0,
"qualification_points": 340.0,
"pending_points": 0.0,
"remaining_points": 160.0,
"level_title": "Gold",
"next_level_title": "Platinum",
"next_level_points": 500.0,
"level_expire_date": "2027-06-16T00:00:00"
}
FieldMeaning
pointsThe customer-facing spendable balance
qualification_pointsPoints earned inside the current qualification window, which ends at level_expire_date. Tier renewal is assessed on this
pending_pointsPoints earned but not yet released for spending
level_titleCurrent tier
next_level_titleThe tier above, or empty at the top tier
next_level_pointsThe qualification_points threshold for the next tier
remaining_pointsQualification points still needed for the next tier: next_level_points − qualification_points

remaining_points is not a balance, and showing it as one is a visible bug. It can also go negative once the member has passed the threshold — treat anything at or below zero as “reached”.

At the top tier there is no next level: next_level_title is empty and next_level_points is 0. Hide the progress display rather than dividing by zero.

We award points when you post a transaction. You do not add them yourself.


  • All properties are snake_case.
  • Enums are strings: "OnlineStore", "Percentage", "NotShared".
  • Dates are ISO 8601: 2026-09-10T12:30:00Z. See the timezone note.
  • Content-Type: application/json on every request with a body.
  • Money is a JSON number, not a string: 499.00, not "499.00".
  • country_code on an address is ISO alpha-2 (NO, SE). country_code on a phone is a dial code (+47). Same name, different meaning, and they sit next to each other in the payload.