Skip to content

Quickstart

Goal: a real member in Diller, with a real order against them, in about fifteen minutes. Sandbox only — nothing here touches production.

You need a client id and client secret. Your merchant finds them in Diller under Settings → Our Details → API Details. See Authentication.

Throughout, $STORE_ID is the store you are working against. Step 2 shows you how to find it.

Terminal window
export DILLER_BASE="https://api.prerelease.dillerapp.com"
export CLIENT_ID="your-client-id"
export CLIENT_SECRET="your-client-secret"

Terminal window
curl -s -X POST "$DILLER_BASE/connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "scope=api.retailer" \
-d "client_id=$CLIENT_ID" \
-d "client_secret=$CLIENT_SECRET"
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"scope": "api.retailer",
"expires_in": 3600,
"token_type": "Bearer"
}

Save it:

Terminal window
export TOKEN="paste-the-access_token-here"

Every call from here carries Authorization: Bearer $TOKEN.


Every resource URL needs a store id. Ask the API for the store your credentials belong to:

Terminal window
curl -s "$DILLER_BASE/api/v2.0/stores" \
-H "Authorization: Bearer $TOKEN"
[
{
"id": "Ab3xK_9pQrLm",
"store_name": "Nordmann Sport Oslo",
"currency": "NOK",
"time_zone": "Europe/Oslo"
}
]
Terminal window
export STORE_ID="Ab3xK_9pQrLm"

The array always holds exactly one store, and its id matches your client id. If the token request failed instead, check you are not mixing sandbox and production credentials.


Terminal window
curl -s "$DILLER_BASE/api/v2.0/stores/$STORE_ID/departments" \
-H "Authorization: Bearer $TOKEN"
[
{ "id": "101", "name": "Web" },
{ "id": "102", "name": "Oslo" }
]

Pick the one representing your webshop and keep it. Everything below uses 101.


This is the ecommerce signup call: you have already collected consent on your own form, so you create the member directly.

Terminal window
curl -s -X POST "$DILLER_BASE/api/v2.0/stores/$STORE_ID/members" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"phone": { "country_code": "+47", "number": "99900001" },
"first_name": "Ola",
"last_name": "Nordmann",
"email": "ola.nordmann@example.com",
"department_ids": ["101"],
"consent": {
"gdpr_accepted": true,
"save_order_history": true,
"receive_sms": true,
"receive_email": true
},
"origin": {
"system_id": "my-webshop",
"department_id": "101",
"channel": "OnlineStore"
}
}'

You get back 200 with the new member. Keep the id — that is the member id.

Send both receive_sms and receive_email, as above. Leave either one out and it defaults to true, opting the member in to marketing they never agreed to — see consent.

{
"id": "m_nsr714RRBEHX",
"first_name": "Ola",
"last_name": "Nordmann",
"email": "ola.nordmann@example.com",
"phone": { "country_code": "+47", "number": "99900001" },
"consent": {
"gdpr_accepted": true,
"save_order_history": true,
"receive_sms": true,
"receive_email": true
},
"membership_detail": {
"points": 0.0,
"qualification_points": 0.0,
"pending_points": 0.0,
"remaining_points": 0.0,
"level_title": null,
"next_level_title": null,
"next_level_points": 0.0,
"level_expire_date": null
},
"department_ids": ["101"],
"segments": []
}
Terminal window
export MEMBER_ID="m_nsr714RRBEHX"

This is what you will call on every login.

Terminal window
curl -s -G "$DILLER_BASE/api/v2.0/stores/$STORE_ID/members/search" \
--data-urlencode "phone=+4799900001" \
-H "Authorization: Bearer $TOKEN"

Returns an array:

[ { "id": "m_nsr714RRBEHX", "first_name": "Ola", "...": "..." } ]

Terminal window
curl -s -X POST "$DILLER_BASE/api/v2.0/stores/$STORE_ID/members/$MEMBER_ID/transactions" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"external_id": "ORDER-1001",
"created_at": "2026-09-10T12:30:00Z",
"currency": "NOK",
"total": 499.00,
"total_tax": 99.80,
"total_discount": 0,
"round_off_amount": 0,
"send_email_receipt": false,
"payment_details": [ { "payment_method": "Card", "sub_total": 499.00 } ],
"department_id": "101",
"origin": {
"system_id": "my-webshop",
"department_id": "101",
"channel": "OnlineStore"
},
"coupon_codes": [],
"details": [
{
"product": {
"external_id": "SKU-123",
"sku": "SKU-123",
"name": "T-shirt, large"
},
"quantity": 1,
"unit_price": 499.00,
"tax_percentage": 25,
"discount": 0,
"total_price": 499.00,
"coupon_codes": []
}
]
}'
{ "is_ok": true, "details": "" }

The status is 200 whether or not the order was recorded, so check is_ok. Run the same request twice and the second one comes back is_ok: false — we do not record the same order twice.

Fetch the member again and membership_detail.points will have moved.


origin.channel.

It is optional. It is nullable. And if you leave it out — or misspell it — we silently record the action as point-of-sale. Your merchant’s reporting will show zero online activity while orders quietly pile up in the wrong channel.

"origin": { "channel": "OnlineStore" }

The literal string is "OnlineStore". On a transaction, only that exact value maps to the webshop channel; anything else, including "Online", "Web" or "Storefront", falls back to POS. Member creation is more forgiving and recognises some looser spellings, but do not lean on that — use the one value that works everywhere.

Set it on every call that accepts an origin: member create, member update, transaction, cancel, refund, coupon reservation.


sequenceDiagram autonumber participant S as Your server participant D as Diller API S->>D: POST /connect/token D-->>S: access_token (1 hour) S->>D: GET /stores/{storeId}/departments D-->>S: [ { id: "101", name: "Web" } ] S->>D: POST /stores/{storeId}/members D-->>S: 200 MemberResponse { id } S->>D: GET /members/search?phone=... D-->>S: [ MemberResponse ] S->>D: POST /members/{memberId}/transactions D-->>S: { is_ok: true }

That is the whole skeleton. The rest of the documentation fills it in: matching existing customers on login, consent, coupons at checkout, and what to do when an order is cancelled.