Stores & drivers
Stores & staff
How merchant owners, staff and stores relate, the store settings that change what customers see, and managing staff accounts.
A merchant account is either a store owner or a staff member. Owners run one or more stores; staff work in exactly one store.
flowchart LR
owner["Owner<br/>merchant, role: owner"]
owner --> a["Store A"]
owner --> b["Store B"]
a --> a1["Manager"]
a --> a2["Employee"]
b --> b1["Employee"]
Owners and staff#
| Owner | Staff (manager, employee) |
|
|---|---|---|
| Created by | Registering | The owner, from the merchant app |
| Stores | Every store they own | The one store in their store_id |
| Logs in with | Merchant login | The same endpoint |
Store-scoped endpoints take {store} in the URL and are open to the store's owner and any of its staff; anyone else gets 403. Today manager and employee have the same access inside the store — the role is a label for the owner's own organisation.
Managing staff#
Only the owner manages staff:
GET stores/{store}/stafflists a store's staff. Staff members calling it get403.POST stores/{store}/staffcreates a staff account:name,phone,password,role(manageroremployee), optionalemailandis_active. The password needs upper and lower case letters, a number and a symbol. Staff accounts are ready to use at once, with no verification step.PUT staff/{staff}changes name, phone, email, role oris_active.DELETE staff/{staff}removes the account.
Deactivating a staff member (is_active: false) or deleting them signs them out everywhere: their tokens are revoked, so their next request returns 401, and their devices stop receiving pushes. A deactivated account gets the staff_deactivated push first, so the app can explain what happened.
Stores#
GET stores lists the owner's stores; POST stores and PUT stores/{store} create and edit them.
Settings that change what customers see:
| Field | Effect |
|---|---|
name |
Shown everywhere; translatable ({"ar": …, "en": …}) |
working_hours |
Decides is_closed (below) |
min_order_price |
Checkout refuses carts under it (below_min_order_price) |
delivery_range |
Delivery range in km |
delivery_service |
Whether the store delivers |
return_window_days |
Days after delivery a customer may open a Return. null uses the platform default; see Returns |
is_selected |
The store the merchant app opens on. Only one store per owner is selected: selecting one clears the others |
Working hours#
working_hours is keyed by lowercase English day name, with times on the Riyadh clock:
{
"saturday": { "is_enabled": true, "is_full_day": false, "from": "08:00", "to": "23:00" },
"friday": { "is_enabled": true, "is_full_day": false, "from": "16:00", "to": "02:00" },
"sunday": { "is_enabled": true, "is_full_day": true }
}
- A day that is missing or has
is_enabled: falseis closed. is_full_day: trueis open around the clock.toearlier thanfromruns past midnight (the Friday example closes at 02:00 Saturday).- A store with no working hours is always closed, so new stores must set them before customers can see them as open.
Taken offline by Tote#
Tote's operations team can take a store offline for a while — for example when it keeps missing orders. The store shows is_closed: true until the given time, and the store team is pushed store_forced_offline (with the reason and the end time in the message) and later store_back_online.
Location#
A store's position is sent as address.lat and address.lng when the store is created. Customers only find a store when it lies within their search radius; see Location & store discovery.
Legal identity and invoicing#
The store, not Tote, is the seller of what customers buy. Tote issues each customer's invoice in the store's name, so before a store can take orders it needs:
- a legal identity: the legal name and registered address in Arabic and English, the 10-digit CR number and the 15-digit VAT number. Every store must be VAT-registered;
- a signed invoicing agreement, filed and verified by Tote's team.
Until both are verified, checkout at the store answers 422 with code: store_unavailable, the same code an inactive store gets.
Store owners manage the identity (managers and employees get 403):
GET stores/{store}/legal-identityreturnsidentity(the verified details invoices are issued under),pending(a change waiting for review, or the last rejected one with Tote'sdecision_note),authorization(whether the agreement is filed and verified) andcan_sell.PUT stores/{store}/legal-identityproposes new details. The store keeps selling under its verified identity until Tote verifies the change. A verified change applies to orders paid from then on, never to invoices already issued. A newer proposal replaces one still waiting.
Tax and settlement documents#
Store owners download every document they need for their VAT return and to match Tote's transfers. GET stores/{store}/documents takes a type:
type |
What it is |
|---|---|
customer_invoice, customer_credit_note |
The customers' invoices and credit notes Tote issued in the store's name: the store's sales and output VAT |
commission |
Tote's monthly commission invoice and its credit and debit notes to the store (kind): Tote's commission, fees and their VAT, which the store can deduct as input VAT |
settlement_statement |
The statement of each transfer: what Tote collected, what it deducted, what it sent. Not a tax document |
Filter with from / to (Riyadh dates). Each row's pdf_url points at GET stores/{store}/documents/{type}/{document}/pdf; another store's document answers 404. Tote issues the commission invoice for a month on the 1st of the next month.
GET stores/{store}/vat-report with from and to (Riyadh dates) gives the store's figures for its VAT return from those documents: sales (output VAT on the invoices issued in its name, less credit notes) and input (VAT on Tote's commission documents).