POS API · kassa tərtibatçıları üçün

Kassa sistemləri üçün açıq API

Müəssisənin öz kassası Unemi müştərisini tanıyır, onun bonuslarını və mükafatlarını çekə tətbiq edir və ödənişdən sonra məbləği Unemi-yə göndərir — möhür, bonus və ya abunəlik Unemi Staff kassasında olduğu kimi keçirilir. Kassa sistemi tərtibatçıları və öz kassası olan müəssisələr üçün.

Necə qoşulmaq olar

  1. Pro tarifi

    Açıq API Pro tarifində işləyir. Digər tariflərdə açar yaratmaq olmur, əvvəl yaradılmış açarla gələn sorğular isə rədd edilir.

  2. Kabinetdə açar

    Sahib kabinetdə «API и интеграции» (API və inteqrasiyalar) bölməsini açır və açar yaradır: adı — məsələn, kassaya və ya məntəqəyə görə — və hüquqları.

  3. Hüquqlar: bir məntəqə və ya bütün müəssisə

    Məntəqə açarı yalnız həmin məntəqədə işləyir. Bütün müəssisə üçün açar müştərini tanıyanda və çeki ləğv edəndə məntəqəni göstərir — məntəqələrin id-ləri həmin bölmədədir.

  4. Açar yalnız bir dəfə göstərilir

    Onu serverdəki kassa ayarlarına köçürün. Unemi yalnız onun heşini saxlayır: itmiş açarı bərpa etmək olmur — yenisini yaradın, köhnəsini ləğv edin.

Kassa Unemi ilə necə işləyir

Hər çek üçün dörd addım. Müştəri hər çekə ayrıca razılıq verir: telefonundakı QR və ya kod bir çeklik sessiya açır.

  1. Müştəri QR göstərir

    Və ya altındakı altı rəqəmi deyir. Kassa müştərini tanıyır və onun balanslarını, mükafatlarını, kuponlarını və gedən aksiyaları alır.

    posIdentify
  2. Kassir endirimi tətbiq edir

    Bonusların silinməsi, mükafat və ya kupon öz kassanızda adi endirim kimi keçirilir.

  3. Çek bağlandı — məbləğ Unemi-yə

    Kassa çekin nömrəsini, proqramı, məbləği, silinən bonusları və istifadə olunan mükafatları, istəsəniz kassiri və çekdəki məhsulları da göndərir. Müştəriyə Unemi Staff kassasından sonrakı kimi bildiriş gəlir.

    posApply
  4. Ləğv və qaytarma — 48 saat ərzində

    Çek ləğv olunarsa və ya qaytarılarsa, kassa onu Unemi-də də çek nömrəsi ilə ləğv edir: hər şey əvvəlki vəziyyətinə qayıdır. Qismən qaytarmanı kassa məbləği ilə göndərir — Unemi onu müəssisə sahibinə göstərir.

    posUndo

Limitlər

  • Hər açar üçün dəqiqədə 120 sorğu
  • Tanıma sessiyası bir çek keçirir və 2 saat etibarlıdır, kassa açarında — 12 saat
  • Çeki 48 saat ərzində ləğv etmək və ya qaytarmaq olar
  • Bir çekdə bir proqram və 5-ə qədər mükafat və kupon
  • 15 dəqiqə ərzində uğursuz tanımalar: 5-dən sonra 1 dəqiqə, 10-dan sonra 5 dəqiqə, 15-dən sonra 30 dəqiqə fasilə
  • Hər müəssisəyə 10-a qədər aktiv açar

Metodlar kitabçası

Kitabça ingilis dilindədir. Metodların, sahələrin və xəta kodlarının adları istənilən dildə eynidir. Rus dilində kitabça

General rules

ItemValue
Address https://api.unemi.kz/<method>
Methods posIdentify, posApply, posUndo — POST only
Headers Authorization: Bearer unemi_<id>_<secret>, Content-Type: application/json
Body A JSON object; the response is JSON
Time ISO 8601 in UTC: "2026-10-07T09:30:00.000Z"
Money and bonuses Whole tenge; 1 bonus = 1 ₸
Request id Response header X-Unemi-Request-Id (and requestId in errors) — support will ask for it
Location A location key works only at its location; a key for the whole business passes venueId to posIdentify and posUndo, and posApply takes the location from the session
POS key A key for one location with a POS system and the location's rules: 12-hour sessions and a default program — see “POS key”
Limit 120 requests per minute per key
CORS None: the key lives on the POS server, not in a browser
Texts Program, reward, coupon and promotion names come as the business wrote them in its dashboard (the examples use English); level names and error message are in Russian

Session and customer consent

  • The customer consents to every receipt: the QR or six digits from the customer's screen open a session that posts one receipt and lasts 2 hours, 12 hours with a POS key. The next receipt needs a new QR.
  • The QR and the code are single-use: posIdentify redeems them, just like a staff scan. Repeating the same request with the same key (the response got lost) returns the same session — until a receipt is posted on it or it expires; the Unemi till or another key gets code_used.
  • With a POS key, the same code or phone number returns the same open session for 12 hours, even after the code itself has expired (it lives about a minute): the POS asks for the customer's card several times per order. A fresh code of another customer with the same digits wins — it is redeemed and opens a new session.
  • A cancelled receipt (posUndo) doesn't use up the consent: the same session, while it lives, can post it again.
  • A phone number finds only this business's customers (they already have a card, a bonus account or a subscription), and the session is accrual-only: accrualOnly: true, stamps and bonuses without spending.
  • A phone number proves nothing: spending bonuses, buying or using a subscription and redeeming a reward need the QR or code from the customer's phone — otherwise consent_required. The API does not create new customers by phone number.

Retries and idempotency

  • The response got lost — repeat the same request: the operation is not posted twice.
  • posIdentify with the same QR or code from the same key returns the same session until a receipt is posted on it or it expires.
  • posApply with the same receiptId and the same data returns the saved response with replayed: true — no second operation and no second push to the customer. The same receiptId with different data or another session gets receipt_conflict. The cashier and items (cashier, items) are for reference only: they aren't compared.
  • receiptId is unique within a location: if a location has several tills, their receipt numbers must not overlap — for example, prefix them with the till. After a cancellation (posUndo) the same receiptId can be posted again.
  • Repeating posUndo returns the same response with replayed: true.
  • rate_limited and too_many_attempts — wait details.seconds seconds; internal — try again later.

Error response

An error is an HTTP status and a body:

{
  "error": {
    "code": "cooldown",
    "message": "Слишком рано: следующий штамп можно через 12 мин",
    "details": { "minutes": 12 },
    "requestId": "Qk2v9XyZaB7cD1eF3gH5"
  }
}

The POS branches on error.code — the codes are stable (tables below). message is in Russian, as in the Unemi till; details holds the numbers for the code (minutes, limit, balance).

posIdentify — identify the customer

POST https://api.unemi.kz/posIdentify

The QR from the Unemi app, the six digits under it or a phone number → a session for one receipt, the customer, balances, rewards and coupons, running promotions.

Request

FieldDescription
qr The QR code string from the customer's screen: unemi:1:st:… — or the six digits of a digital QR (see “POS key”)
code The six digits under the QR — when there is no scanner
phone The customer's Kazakhstan phone number — accruals only (see “Session and customer consent”)
venueId Location id — for a key for the whole business; optional for a location key

Exactly one of qr, code, phone.

By QR
{ "qr": "unemi:1:st:Xk2pQ9LmNb7Tz1Rw4Ycd", "venueId": "v8Kp2LmQx1" }
By six digits
{ "code": "482913" }
By phone number
{ "phone": "+7 701 123 45 67" }

Response

Response
{
  "session": "Gm3kQ8rT2vXw9LpZa1Yc",
  "expiresAt": "2026-10-07T11:30:00.000Z",
  "identifiedBy": "qr",
  "accrualOnly": false,
  "customer": {
    "id": "cus_4f1c9a0b2d7e8f3a6b5c1d2e",
    "name": "Aigerim"
  },
  "level": {
    "level": 3,
    "name": "Friend",
    "metric": "visits",
    "value": 18
  },
  "selectedProgramId": "p8Fh2kLm",
  "programs": [
    {
      "id": "p8Fh2kLm",
      "type": "stamps",
      "name": "Stamp card",
      "locked": false,
      "minLevel": 1,
      "stamps": {
        "count": 3,
        "required": 6,
        "rewardTitle": "Gift",
        "rewardPercent": 100,
        "steps": [
          { "stamp": 4, "kind": "discount", "percent": 20, "title": "" }
        ]
      }
    },
    {
      "id": "q2Nw7xRt",
      "type": "bonus",
      "name": "Bonus card",
      "locked": false,
      "minLevel": 1,
      "bonus": {
        "balance": 1250,
        "percent": 5,
        "percentBp": 500,
        "levelPercent": 7,
        "levelBp": 700,
        "welcomeBonusPending": false
      }
    },
    {
      "id": "s5Kd1vBn",
      "type": "subscription",
      "name": "Subscription",
      "locked": false,
      "minLevel": 1,
      "subscription": {
        "item": "Item of the day",
        "price": 15000,
        "durationDays": 30,
        "lifetime": false,
        "perDay": 1,
        "pass": {
          "expiresAt": "2026-10-20T07:00:00.000Z",
          "expired": false,
          "leftToday": 1,
          "lifetime": false
        }
      }
    }
  ],
  "rewards": [
    {
      "id": "rW7yT2pQ",
      "kind": "reward",
      "title": "Gift",
      "programId": "p8Fh2kLm",
      "percent": 100,
      "item": null,
      "expiresAt": "2026-10-30T09:00:00.000Z"
    },
    {
      "id": "cP3mN8sD",
      "kind": "coupon",
      "title": "−20% off the order",
      "programId": null,
      "percent": 20,
      "item": null,
      "expiresAt": "2026-10-08T18:00:00.000Z"
    }
  ],
  "promos": [
    {
      "title": "Scheduled offers",
      "description": "Bonuses ×2",
      "until": "2026-10-07T11:00:00.000Z",
      "effect": { "kind": "bonus_multiplier", "value": 2 }
    }
  ]
}
FieldDescription
session, expiresAt The session for posApply and when it ends — in 2 hours, 12 hours with a POS key.
identifiedBy, accrualOnly How the customer was identified: qr (a digital QR too), code or phone. accrualOnly: true — by phone number: accruals only.
customer id — a permanent customer id for this business (the same person has a different one at another business); name — the name as Unemi Staff shows it. The POS gets no phone number, email or internal Unemi id.
level The customer's level if the business has levels turned on; otherwise null. value — the all-time total: days with visits (metric: "visits") or purchases in tenge ("spend"); an earned level never goes down.
stamps.steps Rewards on intermediate stamps of the card, by stamp number: stamp — the stamp number (1 to required; on the last stamp the guest gets both the step reward and the main reward rewardTitle), kind — discount (percent% off) or item (a free item, percent is 100), title — the title; empty — name it by the discount. No rewards — an empty list.
locked, minLevel locked: true — the program starts at a level and the customer's level is below minLevel: an operation on it returns level_required (except a hand-out on an already paid subscription).
bonus.balance How many bonuses can be spent. welcomeBonusPending: true — the first visit: welcome bonuses are credited by this same operation and can already be spent.
bonus.percent, bonus.levelPercent percent — the program's percentage. levelPercent — this customer's percentage for their level, if the business set percentages by level and it is higher than percent: posApply credits at it; otherwise null.
bonus.percentBp, bonus.levelBp The same percentages exactly, in basis points: 250 — 2.5%, 10,000 — 100%. A percentage can be fractional (up to two decimal places, from 0 to 100); percent and levelPercent are integers rounded half up (2.5% → 3) for POS systems connected earlier. Preview the credit by bp: floor((amount − spend) × bp / 10000).
subscription.pass The customer's subscription; null — none bought.
subscription.lifetime A lifetime program: its durationDays is 36,500. pass.lifetime — the customer's lifetime subscription: it ends a hundred years from now and never needs renewing (pass_lifetime_active).
selectedProgramId The program the customer picked in the app before showing the QR; null — staff choose.
rewards Unredeemed stamp card rewards and campaign coupons still valid: percent — the discount (100 — free), item — the coupon's item.
promos Scheduled offers at the location right now. The server applies the effect (bonus_multiplier, bonus_extra_percent, double_stamps) itself on posApply; with no effect (effect: null) it is a reminder for staff to apply the discount.
defaultProgram POS key only: the program a receipt without programId and action goes to — { "programId": "q2Nw7xRt", "action": "bonus" }: the stamp card or bonus card the customer picked in the app, otherwise the key's default program; null — neither. A regular key has no such field.

posApply — post a closed receipt

POST https://api.unemi.kz/posApply

The receipt is closed and paid → the program operation and redeemed rewards. The operation, reward redemptions, the visit for levels, “Bring a friend” gifts and gifts for a new level go in one transaction: all or nothing.

Request

FieldDescription
session From the posIdentify response.
receiptId The receipt id in your POS: 1–64 printable ASCII characters without spaces, or a non-negative integer. The idempotency key — within a location.
programId, action A program operation — as a pair (actions below). Without the pair — rewards redemption only, and with a POS key a receipt with amount goes to the default program.
amount The receipt amount, ₸. Required for bonus (1 … 10 000 000); optional for the others (a whole number 0 … 10 000 000) and stored for disputes.
spend Bonuses spent on this receipt; bonus only (with a POS key and no program too — then the default program must be a bonus card), no more than amount and the balance.
rewardIds Up to 5 rewards and coupons from posIdentify applied to the receipt.
cashier Optional: who posted the receipt — { "id": "1234", "name": "B. Vdovin" }; id — the employee code in your POS (up to 64 characters without spaces), the name — up to 100 characters. Stored with the receipt for disputes and fraud checks.
items Optional: up to 100 lines — { "code": "7644", "name": "Item of the day", "qty": 2, "amount": 2400 }; the POS item code — up to 40 characters, the quantity above zero (fractional for goods sold by weight), the amount after discounts in whole tenge. Needed for a POS key's “stamp only for these items” rule.

One receipt — one program and up to 5 rewards and coupons.

A bonus card receipt: bonuses spent and a coupon
{
  "session": "Gm3kQ8rT2vXw9LpZa1Yc",
  "receiptId": "A-1024",
  "programId": "q2Nw7xRt",
  "action": "bonus",
  "amount": 4500,
  "spend": 500,
  "rewardIds": ["cP3mN8sD"]
}

Program actions

actionProgramWhat it does
stamp Stamp card A stamp on the customer's card
bonus Bonus card Bonuses on the part paid in money, and spending spend
pass_purchase Subscription Buy or renew a subscription
pass_issue Subscription Hand out on a subscription

Bonuses are credited on the part paid in money (amount − spend) at the program's percentage, rounded down to a whole bonus — floor((amount − spend) × bp / 10000) — or — for a customer whose level gives more — at their level's percentage before this receipt (bonus.levelBp from posIdentify); with the scheduled offers effect if one is running. The level and promotion additions add up rather than multiply, and together never exceed the amount paid.

A POS key without a program. A receipt with amount but without the programId and action pair goes to the default program: the stamp card or bonus card the customer picked in the app before showing the QR, otherwise the program and action from the key's rules.

Rule thresholds: paid in money (amount − spend) below the threshold — no accrual; a stamp of a default program with item codes needs at least one line with such a code (no items — no stamp). The threshold never stops spending bonuses, but without a bonus program spending is impossible — action_mismatch.

Nothing accrued — the response is still a success: operation: null and the reason in skipped; the receipt is recorded and the session closed. A regular key has no skipped field, and a receipt without the pair and without rewards gets invalid_request.

Response

Response
{
  "receiptId": "A-1024",
  "operationId": "bonus:Zx81LmQa0pTt7uVv3wYb",
  "operation": {
    "type": "bonus",
    "programId": "q2Nw7xRt",
    "programName": "Bonus card",
    "amount": 4500,
    "spent": 500,
    "earned": 480,
    "welcome": 0,
    "balance": 1230,
    "promo": {
      "title": "Scheduled offers",
      "kind": "bonus_multiplier",
      "value": 2,
      "extra": 200
    },
    "levelPercent": 7,
    "levelBp": 700,
    "levelExtra": 80
  },
  "redeemed": [
    {
      "id": "cP3mN8sD",
      "kind": "coupon",
      "title": "−20% off the order"
    }
  ],
  "referral": null,
  "levelUp": null,
  "replayed": false
}
operation of a stamp
{
  "type": "stamp",
  "programId": "p8Fh2kLm",
  "programName": "Stamp card",
  "stamps": 4,
  "required": 6,
  "added": 1,
  "rewardIssued": false,
  "rewardTitle": null,
  "stepRewards": [{ "stamp": 4, "title": "20% off", "percent": 20 }],
  "promo": null
}
operation of a subscription purchase
{
  "type": "pass_purchase",
  "programId": "s5Kd1vBn",
  "programName": "Subscription",
  "item": "Item of the day",
  "price": 15000,
  "expiresAt": "2026-11-19T07:00:00.000Z",
  "renewed": true,
  "lifetime": false
}
operation of a subscription hand-out
{
  "type": "pass_issue",
  "programId": "s5Kd1vBn",
  "programName": "Subscription",
  "item": "Item of the day",
  "leftToday": 0,
  "perDay": 1,
  "expiresAt": "2026-10-20T07:00:00.000Z",
  "lifetime": false
}
levelUp with gifts: a big purchase moved the customer straight over a level (levels by purchase amount)
{
  "from": 1,
  "to": 3,
  "name": "Friend",
  "gifts": [
    {
      "level": 2,
      "kind": "coupon",
      "rewardId": "cL4vR9kTq2",
      "title": "−15% off the order",
      "percent": 15,
      "item": null,
      "expiresAt": "2026-11-05T18:59:59.000Z"
    },
    {
      "level": 3,
      "kind": "bonus",
      "programId": "q2Nw7xRt",
      "programName": "Bonus card",
      "amount": 500
    }
  ]
}
FieldDescription
operationId, operation The posted operation; null — the receipt only redeemed rewards or, with a POS key, there was nothing to accrue (see skipped).
promo Scheduled offers that applied to the operation: extra — how many bonuses or stamps the promotion added.
levelPercent, levelBp, levelExtra For a bonus operation: the customer's level percentage, if bonuses were credited at it (levelBp — exactly, in basis points; levelPercent — as an integer), and how many bonuses it added on top of the program's percentage (earned already includes them); otherwise null, null and 0. In the example: a Friend gets 7% instead of 5% and “bonuses ×2” on ₸4,000: 280 for the level + 200 from the promotion = 480, not 560.
rewardIssued true — the stamp completed the card and the customer has a new reward (rewardTitle); it can be redeemed on the next receipt.
stepRewards Rewards for intermediate stamps issued by this receipt: stamp number stamp, title title (without a title — “20% off” or “Gift”), discount percent (100 — a free item). Usually empty or one. These are regular rewards: they come in rewards on identification and can be redeemed on the next receipt.
redeemed Rewards and coupons redeemed from rewardIds.
referral The first visit of an invited friend: { "gift": { "kind": "bonus", "amount": 300, "programName": "Bonus card" } }; otherwise null.
levelUp The customer moved up a level: from, to, name and gifts — gifts for the new level given by this receipt (empty — no gifts; over a level — one per step); otherwise null. A coupon: rewardId — the reward id (it also comes in rewards on identification and can be redeemed on the next receipt), title, a discount percent or an item item, valid until expiresAt. Bonuses: programId, programName, amount — already on the customer's balance (if on this receipt's card — in operation.balance too).
skipped POS key only, for a receipt without a program: why nothing was accrued — no_program (the key has no default program and the customer didn't pick one), below_minimum (paid less than the threshold), no_qualifying_items (no items from the rule on the receipt); null — accrued.
replayed true — a repeated receipt: the saved response, no second operation.

posUndo — cancel a receipt

POST https://api.unemi.kz/posUndo

A cancellation puts everything back as it was before the receipt: the stamp, bonuses, subscription, redeemed rewards and coupons, the level visit, “Bring a friend” gifts and gifts for a new level (a gift coupon the customer has already redeemed stays with them; spent gift bonuses are taken back as far as the balance allows). Allowed within 48 hours after the receipt: voids and refunds happen at the end of a shift and the next day too.

Request

FieldDescription
receiptId The id of a receipt posted with posApply at this location.
venueId Only for a key for the whole business.
amount Optional: the refund amount, ₸ (a whole number 0 … 10 000 000). Less than the receipt amount — a partial refund; without it or with the full amount — the whole receipt is cancelled.
Cancel a receipt
{ "receiptId": "A-1024", "venueId": "v8Kp2LmQx1" }

Repeating a cancellation returns the same response with replayed: true. The receipt's session opens again while it lives: the same receipt can be posted again on the same session.

A receipt can't be cancelled if the customer's card has changed since (undo_not_last), the credited bonuses are spent (undo_bonus_spent), the reward for this stamp has been handed out (undo_reward_redeemed) or the bought subscription has been used (undo_pass_used).

A partial refund (amount below the receipt amount): the operation stays and the response is a success — partial: true, nothing cancelled.

A partial refund and the refusals undo_too_late, undo_not_last, undo_bonus_spent, undo_reward_redeemed, undo_pass_used, operation_not_found are recorded for the owner in the dashboard's refund log, and — if they turned on suspicious operation alerts — as a signal in the morning summary too. Don't block the refund in your POS because of the posUndo response: a refusal code means Unemi didn't reverse the accrual, and the owner decides. A successful cancellation removes the record from the log.

Response

Response
{
  "receiptId": "A-1024",
  "undone": ["bonus:Zx81LmQa0pTt7uVv3wYb", "redeem:cP3mN8sD"],
  "undoneAt": "2026-10-07T09:41:12.000Z",
  "replayed": false
}
Partial refund
{
  "receiptId": "A-1024",
  "undone": [],
  "undoneAt": null,
  "partial": true,
  "replayed": false
}
FieldDescription
undone What was cancelled: the operation id and redemptions (redeem:<reward id>); empty — a partial refund.
undoneAt When it was cancelled; null — a partial refund.
partial true — a partial refund: nothing cancelled, recorded for the owner. A full cancellation has no such field.
replayed true — a repeated cancellation: the saved response.

Error codes

Access and request

CodeHTTPWhendetails
unauthorized 401 No key, the key doesn't look like an Unemi key or doesn't match
key_revoked 401 The key was revoked in the dashboard
plan_feature 403 The business isn't on Pro (or the payment is more than 7 days overdue) plan, feature
method_not_allowed 405 Not POST
invalid_request 400 The body isn't a JSON object or a field has the wrong form field
rate_limited 429 More than 120 requests per minute per key, or 30 failed sign-ins from one address per minute seconds, limit
venue_required 400 A key for the whole business, but no venueId field
venue_forbidden 403 A location key, but the request is about another location
venue_not_found 404 The location doesn't exist, belongs to someone else or is turned off
internal 500 A failure on Unemi's side — try again later

Identification (posIdentify)

CodeHTTPWhendetails
unknown_qr 400 Neither an Unemi QR nor six digits
reward_qr 400 A reward QR was shown — the card QR is needed; rewards come in rewards
invalid_phone 400 Not a Kazakhstan phone number
code_not_found 404 No such code (a typo or someone else's QR)
code_used 409 The code has already been redeemed
code_expired 409 The code is outdated (it lives about a minute) — the customer refreshes the screen
customer_not_found 404 No customer of this business with that phone number
customer_unknown 400 The code has no customer
too_many_attempts 429 A pause after failed identifications seconds

Receipt (posApply)

CodeHTTPWhendetails
session_not_found 404 No such session, it belongs to someone else or to a location the key can't access
session_expired 409 The session is older than 2 hours (12 with a POS key)
session_used 409 Another receipt has already been posted on this session
receipt_conflict 409 This receipt has already been posted with different data operationId
program_not_found 404 No such program or it belongs to someone else
program_paused 409 The program is turned off
action_mismatch 409 The action doesn't fit the program type; with a POS key — bonuses spent on a receipt without a default bonus program type
level_required 409 The program starts at a level, the customer's level is lower minLevel, level
cooldown 409 This card got a stamp at this location recently minutes
daily_limit 409 The daily stamp limit limit
invalid_amount 400 The amount or bonuses spent aren't a whole number within the bounds max
bonus_insufficient 409 Spending more than the balance balance
pass_not_found 409 A hand-out without a subscription
pass_expired 409 The subscription has expired expiredAtMillis
pass_day_limit 409 Today's subscription hand-outs are used up limit
pass_lifetime_active 409 The customer already has a lifetime subscription — no need to buy it again
reward_not_found 404 No such reward or it belongs to another customer
reward_other_business 403 A reward of another business
reward_already_redeemed 409 The reward has already been redeemed
reward_expired 409 The reward has expired
coupon_exhausted 409 The first N customers have already used the coupon limit

Cancellation (posUndo)

CodeHTTPWhendetails
receipt_not_found 404 The receipt wasn't posted through the API at this location
invalid_amount 400 The refund amount isn't a whole number within the bounds max
undo_too_late 409 More than 48 hours have passed (minutes is the cancellation window, 2880) minutes
undo_already 409 The operation was already cancelled in the till or the dashboard
undo_not_last 409 The customer's card has changed since the receipt
undo_reward_redeemed 409 The reward for this stamp has already been handed out
undo_bonus_spent 409 The credited bonuses have already been spent balance
undo_pass_used 409 The subscription has already been used
operation_not_found 404 The receipt's operation no longer exists (the reward was deleted)

Security and data

  • The key is unemi_<key id, 20 characters>_<secret, 48 hex>, 192 random bits of secret. Unemi stores only the sha256 of the secret and its last four characters to recognise it in the list; a wrong, malformed or non-existent key gets the same unauthorized.
  • Other businesses are out of reach: a key is bound to its business, sessions and receipts are looked up only there, and a location key sees only its location. Someone else's session, program, reward or receipt answers “not found”.
  • Personal data: the POS gets the customer's name (as Unemi Staff shows it), balances, rewards and level. Responses carry no phone number, email or internal Unemi id; the customer id is a separate alias for each business.
  • Guessing codes and numbers: failed identifications (code_not_found, customer_not_found) add up per key over 15 minutes; after 5 — a 1-minute pause, after 10 — 5 minutes, after 15 — 30 minutes (too_many_attempts). The pause doesn't stop posting or cancelling receipts.
  • The request log in the dashboard has no customer data and is kept for 30 days.
  • There is no sandbox: integrations are tested on a test business on the Pro plan.

POS key

  • A POS key is an API key for one location marked with a POS system: r_keeper, iiko, Syrve or a chain's own POS. The owner creates it in the dashboard by choosing an r_keeper till key. There are no POS keys for the whole business: a POS stands at a particular location.
  • The key carries the location's rules — a default program and action (a stamp for a stamp card, bonuses for a bonus card), an amount threshold (0 … 10 000 000 ₸) and POS item codes (up to 100, each up to 40 characters without spaces) — and the language of texts on the POS screen, one of Unemi's six languages. The rules and the language change without a new key; another POS system needs a new key.
  • What differs in the methods: a session lasts 12 hours, and the same code or phone number within an order returns the same session; posIdentify returns defaultProgram; a receipt without a program goes to the default program, and if nothing was accrued, the reason is in skipped.
  • Digital QR: while the business has an active r_keeper POS key, the QR on the customer's card holds the same six digits as under it — the r_keeper scanner reads them as a card number. Pass them to posIdentify in qr (identifiedBy: "qr"). The general QR from the app menu (not tied to a card) stays as it is: the customer reads out the six digits under it.
What the owner sets in the dashboard: name, location, POS system, language and rules
{
  "name": "r_keeper · Abay",
  "venueId": "v8Kp2LmQx1",
  "pos": {
    "system": "r_keeper",
    "language": "ru",
    "rules": {
      "programId": "q2Nw7xRt",
      "action": "bonus",
      "minAmount": 0,
      "productCodes": []
    }
  }
}

r_keeper bridge for dealers Coming soon

The HttpFarCard module (ExtDllHTTP.dll) of the r_keeper POS server sends POST requests with XML itself — a card request (getcardinfoex) and the transactions of a closed receipt (transactionsex) — and the posRkeeper bridge runs them through the same core as the methods above: it identifies the customer, posts the receipt and cancels it on a rollback or refund.

ItemValue
Address https://api.unemi.kz/posRkeeper — the Address line in ExtDllHttp.ini
Handlers GetCardInfoEx=getcardinfoex, TransactionsEx=transactionsex
Sign-in Basic authentication: login unemi_<key id>, password — the key's secret; https only, otherwise https_required
Key An r_keeper POS key for one location: the owner gets the address, login and password in the dashboard when creating the key and passes them to the dealer. A regular API key or another POS system's key gets pos_key_required
Binding The first request remembers the r_keeper object (its codes in the POS requests); a request from another object gets venue_mismatch
Responses HttpFarCard protocol XML. Refused transactions — Result="1" with the reason in TRRESPONSE; a card not accepted — Locked="1" with the reason in WhyLock

Transactions are refused only when a bonus payment can't be accepted (for example, not enough bonuses, the customer was found by phone number or there is no bonus program): a receipt without a bonus payment always closes, and if the accrual didn't go through, the owner sees the reason in the request log.

Payment rollbacks, refunds and refund rollbacks never stop the POS: a refund cancels the receipt within 48 hours, and what Unemi didn't reverse, the owner sees in the refund log. Gifts and coupons aren't redeemed through r_keeper — they are handed out in the Unemi till. Texts on the POS screen are in the key's language.