OnetapBusiness OS
Developers

API reference

A read-only HTTP API over your published catalogue. Every endpoint, scope and limit below is generated from the same registry Onetap Business OS answers from, so this page cannot drift from the product.

Getting a key

Settings → API Keys. Name the key, choose its scopes and an optional expiry. The key is shown once — it is stored only as a hash, so we cannot show it to you again and neither can anyone who reaches our database. Lost one is replaced, never recovered.

curl https://{your-domain}/api/v1/products \
  -H "Authorization: Bearer pk_live_…"

# X-API-Key is accepted too, for tooling that only offers that header:
curl https://{your-domain}/api/v1/products -H "X-API-Key: pk_live_…"

Authentication

Optional. Without a key, every endpoint answers for the business that owns the domain you called. Send a key — `Authorization: Bearer pk_…` or `X-API-Key: pk_…` — to ask for a specific business's catalogue from anywhere. A key does not raise your rate limit. Keys are read-only and identify a business, never a user, so they can never reach anything behind a person's permissions.

Scopes

There are three, and all three are reads. That is a property of the design rather than a stage we are at: a key identifies a business and carries no user identity, so it can never reach anything gated on a person's permissions. There is no write API.

catalog:readSearch your catalog and read its filter options
products:readRead your published products
categories:readRead your published category tree

Endpoints

Rate limits are per endpoint, not one shared budget. Exceeding one returns 429 with a Retry-After header.

GET/api/v1

This description — endpoints, scopes, limits and caching.

no scopecache 3600s
GET/api/v1/products

Published products, paginated, searchable and filterable.

scope products:read100/mincache 60s
qFree-text match on name, description, SKU or brand. `search` is accepted as an alias.
categoryCategory id. Returns products filed directly under it.
tagTag slug.
brandBrand name, matched case-insensitively.
featured`true` to return only featured products.
sortOrdering. Defaults to `featured`.
pagePage number, from 1. Defaults to 1.
limitItems per page, maximum 100. Defaults to 25.
GET/api/v1/products/:slug

One published product by its slug. 404 when the slug is not published for this tenant.

scope products:read200/mincache 300s
slug *The product's URL slug, in the path.
GET/api/v1/products/:slug/recommendations

Related products for one product, grouped as crossSell, upsell, related, coBuying and similar.

scope products:read60/mincache 120s
slug *The product's URL slug, in the path.
GET/api/v1/categories

The published category tree, nested by default.

scope categories:read60/mincache 300s
flat`true` returns a flat array instead of a nested tree.
GET/api/v1/search

Search across products, categories and tags at once.

scope catalog:read60/mincache 60s
q *The search text. Required — an empty query is a 400, not an empty result.
type`products`, `categories`, `tags` or `all`. Defaults to `all`.
pagePage number, from 1. Defaults to 1.
limitItems per page, maximum 100. Defaults to 25.
GET/api/v1/filters

Filter options — categories, brands, tags, price range, availability — with live counts for the current result set.

scope catalog:read30/mincache 120s
categoryNarrow the counts to one category id.
qNarrow the counts to a search term.

Responses

{
  "success": true,
  "data": { … },
  "meta": { "version": "1", "timestamp": "2026-09-11T04:00:00.000Z" }
}

// and on failure — the status code carries the same news
{ "success": false, "error": "Product not found", "meta": { "version": "1" } }
  • All prices are in INR.
  • Only published, catalogue-visible products are returned. Nothing behind a login is reachable with a key.
  • Responses are CORS-open, so a browser can call them directly.
  • Every response carries `success`, `data` and `meta`; an error carries `success: false` and `error`.
  • Rate limits are per endpoint, not one budget — see each endpoint below. Exceeding one returns 429 with `Retry-After`.

Caching

  • An anonymous response is publicly cacheable — the domain you called is part of the cache key, so a shared cache cannot hand your answer to another business.
  • A key-resolved response is `private, no-store`. A header is NOT part of a shared cache key, so caching one would risk serving your data to the next caller of the same URL.
  • Both forms send `Vary` on the authentication headers as well.

What this API does not do

  • No writes. Every scope is a read. You cannot create or change an invoice, a party, a product or stock through a key.
  • Catalogue only. The endpoints above are the whole surface. CRM, sales, purchase, finance and inventory are not reachable with a key.
  • No outbound webhooks and no SDK. Poll the endpoints, and call them over plain HTTP from whatever you already use.
Start 7-Day Trial — ₹99