Tote developers
All guides

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}/staff lists a store's staff. Staff members calling it get 403.
  • POST stores/{store}/staff creates a staff account: name, phone, password, role (manager or employee), optional email and is_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 or is_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: false is closed.
  • is_full_day: true is open around the clock.
  • to earlier than from runs 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.

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:

  1. 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;
  2. 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-identity returns identity (the verified details invoices are issued under), pending (a change waiting for review, or the last rejected one with Tote's decision_note), authorization (whether the agreement is filed and verified) and can_sell.
  • PUT stores/{store}/legal-identity proposes 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).