Use Simply Club from your own UI — your own login screen, app, or backend — no embedded JavaScript widget required. Everything the Drop-in does over the wire is a plain REST call you can make yourself: log a shopper in with phone + OTP, get a session token, and read their member profile and benefits.
In short — Logging a shopper in is two requests: the first texts them a one-time code, the second sends that code back and returns a session token. You attach that token to every later request to act as that shopper. That’s the whole integration — the exact URLs are in the steps below.
Building your own UI
This is the path for when you want your own login screen — a React component, a Vue page, a native mobile screen — instead of our widget. You draw the UI; you call these endpoints behind it. In practice it’s two screens:
- Phone screen — a phone input + a “Send code” button → calls
get-otp(Step 1 below). - Code screen — a code input + a “Verify” button → calls
validate-otp(Step 2 below). On success you hold theaccesstoken and the shopper is logged in.
A few things that make this safe and easy from the front end:
- In a browser it just works. If your page is served from an allow-listed domain, the browser sends the
Originheader for you and CORS is already open to that domain — no extra config. - There are no API keys or secrets in this flow, so it’s safe to run entirely client-side. The shopper’s OTP is their credential.
- Store the
accesstoken like any session token (e.g.localStorageor a cookie) and send it asAuthorization: Beareron later calls.
The rest of this page is the exact calls those two screens make.
The full cycle
Login is just the first box. A complete rewarded checkout — the same thing the widget does — is five steps. Every call after login carries your Authorization: Bearer <access> token (and the Origin header). The boxes flow top to bottom; each one names the endpoint and what you get back:
┌──────────────────────────────────────────────┐
│ 1. LOG IN │
│ POST /users/fast/get-otp/:poiId/:phone │
│ POST /users/fast/validate-otp/:poiId │
│ ⇒ access token (90 days) │
└───────────────────────┬────────────────────────┘
│ send the cart
▼
┌──────────────────────────────────────────────┐
│ 2. START A TRANSACTION │
│ POST /transaction/create/:poiId │
│ body: { items: [...] } │
│ ⇒ available promos + benefits + totals │
└───────────────────────┬────────────────────────┘
│ shopper picks benefits in your UI
▼
┌──────────────────────────────────────────────┐
│ 3. LOCK THE SELECTION │
│ POST /transaction/select-and-lock-benefits/ │
│ :poiId │
│ ⇒ confirmed discount for this cart │
└───────────────────────┬────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ 4. GET THE TRANSACTION CODE │
│ GET /transaction/get-transaction-code/:poiId │
│ ⇒ { code } → the sc_transaction_code. │
│ Save it on your order. │
└───────────────────────┬────────────────────────┘
│ shopper pays on your checkout
▼
┌──────────────────────────────────────────────┐
│ 5. AWARD POINTS (from your backend) │
│ POST /transaction/order-webhook │
│ body: order details + sc_transaction_code │
│ ⇒ points awarded, transaction closed │
└──────────────────────────────────────────────┘
| Step | Endpoint | What it’s for | Documented in |
|---|---|---|---|
| 1 | fast/get-otp + fast/validate-otp | Log the shopper in, get a session token. | §3 below |
| 2 | POST /api/v1/transaction/create/:poiId | Send the cart; get the promos/benefits the shopper qualifies for. | §5.1 below |
| 3 | POST /api/v1/transaction/select-and-lock-benefits/:poiId | Lock the benefits the shopper chose. | §5.2 below |
| 4 | GET /api/v1/transaction/get-transaction-code/:poiId | Get the sc_transaction_code to attach to your order. | §5.3 below |
| 5 | POST /api/v1/transaction/order-webhook | After payment, award points and close the transaction. | Webhook Integration Guide |
Steps 2–4 are the part the widget normally runs for you via its benefits engine — full detail in §5 (Run a rewarded checkout). They all take the same session
Bearertoken from step 1, so you can drive the entire flow from your own client. Step 5 is fired by your backend after the shopper pays — without it the shopper sees the discount but no points are awarded.
1. What you need
| # | Requirement | How to get it |
|---|---|---|
| 1 | A poiId | From your Simply Club account manager — an opaque id like 6821f55303f86ff040461ef4. |
| 2 | Fast login enabled for that POI | You control this yourself — sign in to the Dashboard as the POI admin and flip the fast login toggle in your POI’s Settings. (It sets settings.fastLoginEnabled on the POI; without it the fast routes return 403.) |
| 3 | Your calling domain on the POI’s allowed list | If the POI restricts domains, give support the hostname(s) you’ll send as the Origin header. See the Origin rule below. |
There is nothing to install. You’re just calling HTTPS endpoints.
Base URL
| Environment | BASE_URL |
|---|---|
| Production | https://dropins.simplyclub.co.il |
| Staging | https://simply-drop-ins-2.onrender.com |
Enabling fast login yourself
You don’t need to ask anyone — a POI admin turns it on. The easiest way is the Dashboard: sign in, open your POI’s Settings, and toggle fast login on.
If you’d rather automate it, it’s two API calls:
# 1) Log in as the POI admin (env = test | prod)
POST {BASE_URL}/api/v1/poi/login/:env
Content-Type: application/json
{ "accountId": 2886, "username": "your-admin-user", "password": "your-password" }
The response includes your poiId and an admin token:
{ "success": true, "data": { "token": { "token": "<admin-token>" }, "poi": { "_id": "<poiId>" } } }
# 2) Flip the flag — only this field changes, every other setting is left intact
PATCH {BASE_URL}/api/v1/poi/:poiId/settings
Authorization: Bearer <admin-token>
Content-Type: application/json
{ "fastLoginEnabled": true }
That’s it — fast login is now live for that POI.
2. The one rule: send an Origin header
Simply Club scopes a POI to a set of allowed domains. If your POI has domains configured, every request — OTP and authenticated alike — must include an Origin (or Referer) header whose hostname is on that list. Browsers add this automatically; a backend HTTP client does not, so you must set it yourself:
Origin: https://your-allowed-domain.com
- If the POI has no domain restriction (or it’s set to
*), you can skip the header. - If it’s restricted and you omit it, you get
403withORIGIN_REQUIRED. - If you send a hostname that isn’t allowed, you get
403withDOMAIN_NOT_ALLOWED.
Use the same Origin value on the login call and on every call afterward — the session token is bound to it.
3. Log a shopper in (two calls)
Step 1 — Request an OTP
POST {BASE_URL}/api/v1/users/fast/get-otp/:poiId/:phone
Origin: https://your-allowed-domain.com
The server texts a code to the phone and returns an otpChallenge — a one-time token you must echo back in step 2. The code is valid for 120 seconds.
200 response:
{
"success": true,
"data": {
"message": "OTP sent",
"otpChallenge": "<64-hex-char string>",
"isOTPSent": true
},
"error": null
}
Step 2 — Validate the OTP and get a token
POST {BASE_URL}/api/v1/users/fast/validate-otp/:poiId
Origin: https://your-allowed-domain.com
Content-Type: application/json
{
"otp": "123456",
"otpChallenge": "<the otpChallenge from step 1>"
}
200 response:
{
"success": true,
"data": {
"message": "OTP is valid",
"access": "<session token>",
"member": { /* the Simply Club member */ },
"isNewUser": false
},
"error": null
}
access is the session token. Store it — it’s valid for 90 days. isNewUser is true only when the member was just created (a new phone auto-creates a member).
4. Use the token
Put the token in an Authorization: Bearer header (keep the same Origin header too). These are the endpoints you’ll typically call:
| Method & path | Returns |
|---|---|
GET /api/v1/users/my-self/:poiId | The logged-in member’s profile. |
GET /api/v1/users/user-benefits/:poiId | The member’s available benefits. |
POST /api/v1/users/ping/:poiId | `{ authenticated: true |
POST /api/v1/users/logout | Ends the session (invalidates the token). |
Example:
GET {BASE_URL}/api/v1/users/my-self/:poiId
Authorization: Bearer <access>
Origin: https://your-allowed-domain.com
5. Run a rewarded checkout
These are the three calls between login and the order webhook (steps 2–4 of the full cycle). All of them require Authorization: Bearer <access> plus the Origin header, and they operate on the shopper’s open transaction.
The field names
selectedQuntityandmaxQuntityAllowedare spelled exactly like that in the API — match them verbatim.
5.1 Create a transaction (send the cart)
POST {BASE_URL}/api/v1/transaction/create/:poiId
Authorization: Bearer <access>
Origin: https://your-allowed-domain.com
Content-Type: application/json
{
"items": [
{ "id": "1", "sku": "ABC-001", "title": "Olive Oil 500ml", "price": 39.9, "quantity": 2 }
]
}
price is the net unit price after any store-side discounts.
200 response — the benefits the shopper qualifies for:
{
"success": true,
"data": {
"message": "Transaction created",
"serviceTranNumber": "<transaction id>",
"tranDiscountPercent": 0,
"promos": [
{
"id": 12345,
"name": "10% off oils",
"description": "...",
"templateId": 0,
"maxQuntityAllowed": 1,
"unitPrice": 7.98,
"selectedQuntity": 0,
"isAuto": false
}
]
}
}
Each entry in promos is a benefit the shopper can apply. templateId is 0 for a total discount, 1 for cashback; selectedQuntity starts at 0.
5.2 Lock the chosen benefits
Send back the promos the shopper picked — with selectedQuntity set to how many they want — along with the same cart items:
POST {BASE_URL}/api/v1/transaction/select-and-lock-benefits/:poiId
Authorization: Bearer <access>
Origin: https://your-allowed-domain.com
Content-Type: application/json
{
"items": [
{ "id": "1", "sku": "ABC-001", "title": "Olive Oil 500ml", "price": 39.9, "quantity": 2 }
],
"promos": [
{ "id": 12345, "templateId": 0, "unitPrice": 7.98, "selectedQuntity": 1 }
]
}
200 response: { "success": true, "data": { "message": "Benefits locked" } } (or "Tran check completed", depending on POI config) — either confirms the discount for this cart.
This needs the open transaction from 5.1. If there’s no session yet you get
400withSession not found.
5.3 Get the transaction code
GET {BASE_URL}/api/v1/transaction/get-transaction-code/:poiId
Authorization: Bearer <access>
Origin: https://your-allowed-domain.com
200 response:
{ "success": true, "data": { "code": "<sc_transaction_code>" } }
Save code (the sc_transaction_code) on your order — your backend sends it in the webhook below to award points.
5.4 Award points after payment (backend)
Once the shopper pays on your checkout, your backend POSTs the order — including the sc_transaction_code from 5.3 — to close the transaction and award points:
POST {BASE_URL}/api/v1/transaction/order-webhook
The full payload (topic, domain, sc_transaction_code, order_id, customer_id) and signature details are in the Webhook Integration Guide. Without this call the shopper sees the discount but no points are awarded.
6. Full walkthrough with curl (login → checkout)
The whole cycle as one runnable script. Paste the otpChallenge from call 1 into call 2, then the access token from call 2 into ACCESS:
BASE_URL="https://dropins.simplyclub.co.il"
POI_ID="your_poi_id"
PHONE="+972501234567"
ORIGIN="https://your-allowed-domain.com"
# 1) Send the OTP — copy the otpChallenge from the response
curl -s -X POST "$BASE_URL/api/v1/users/fast/get-otp/$POI_ID/$PHONE" \
-H "Origin: $ORIGIN"
# 2) Validate it — returns { data: { access, member } }
curl -s -X POST "$BASE_URL/api/v1/users/fast/validate-otp/$POI_ID" \
-H "Origin: $ORIGIN" \
-H "Content-Type: application/json" \
-d '{ "otp": "123456", "otpChallenge": "PASTE_CHALLENGE_HERE" }'
# Reuse the session token for every call below
ACCESS="PASTE_ACCESS_TOKEN_HERE"
# 3) Create a transaction with the cart — returns { data: { promos: [...] } }
curl -s -X POST "$BASE_URL/api/v1/transaction/create/$POI_ID" \
-H "Origin: $ORIGIN" -H "Authorization: Bearer $ACCESS" \
-H "Content-Type: application/json" \
-d '{ "items": [ { "id": "1", "sku": "ABC-001", "title": "Olive Oil 500ml", "price": 39.9, "quantity": 2 } ] }'
# 4) Lock the benefits the shopper picked (set selectedQuntity)
curl -s -X POST "$BASE_URL/api/v1/transaction/select-and-lock-benefits/$POI_ID" \
-H "Origin: $ORIGIN" -H "Authorization: Bearer $ACCESS" \
-H "Content-Type: application/json" \
-d '{ "items": [ { "id": "1", "sku": "ABC-001", "title": "Olive Oil 500ml", "price": 39.9, "quantity": 2 } ], "promos": [ { "id": 12345, "templateId": 0, "unitPrice": 7.98, "selectedQuntity": 1 } ] }'
# 5) Get the transaction code — save data.code on your order
curl -s "$BASE_URL/api/v1/transaction/get-transaction-code/$POI_ID" \
-H "Origin: $ORIGIN" -H "Authorization: Bearer $ACCESS"
# 6) After the shopper pays, your BACKEND fires the order webhook with that
# code to award points — see §5.4 and the Webhook Integration Guide.
7. When something fails
Every error uses the same envelope — success: false with an errorCode, plus friendlyMessage / friendlyMessageHebrew you can show a shopper. The common ones:
| HTTP | errorCode | Meaning |
|---|---|---|
| 400 | OTP_REQUIRED | No code in the validate body. |
| 404 | OTP_INVALID | Wrong or unknown code. |
| 400 | OTP_EXPIRED | Code older than 120 seconds — request a new one. |
| 403 | OTP_CHALLENGE_MISMATCH | otpChallenge doesn’t match the one from step 1. |
| 401 | (auth) | Missing/expired Authorization token on an authenticated call. |
| 403 | ORIGIN_REQUIRED / DOMAIN_NOT_ALLOWED | Origin header missing or not on the POI’s allowed list. |
| 403 | (POI gate) | fastLoginEnabled is off for this POI. |
The complete error table and security details live in Fast Login.
Related
| If you want to… | Read |
|---|---|
| Skip the OTP screen — your backend already knows the shopper | Login From Backend |
| Embed the full UI instead of calling the API yourself | Getting Started |
| Understand the phone-only fast-login channel in depth | Fast Login |