Skip to main content

1. Overview

The Genuka API is a multi-tenant REST API for building commerce, inventory, messaging, and accounting integrations on top of the Genuka platform. All responses are JSON. Production base URL
There is one environment: production at api.genuka.com. There is no separate sandbox URL.

2. Versioning

The API uses date-based path versioning. Every resource endpoint is nested under the version prefix:
For example:
The version 2023-11 is the current stable version. Breaking changes will be introduced under a new date prefix while the previous version remains accessible during a deprecation period.

3. Authentication

The API uses Laravel Passport personal access tokens (Bearer JWT). You must include a valid token on every authenticated request.

Obtaining a token

Send a POST request to the login endpoint:
A successful response returns a plainTextToken (the token you store and reuse) along with the user’s companyId:
Store plainTextToken securely. Include it in every subsequent request as a Bearer token. For full authentication details — registration, PIN login, password reset, multi-company users — see 02-authentication.md.

4. Required Headers

Every authenticated request to the admin API must include all four headers below. Example authenticated request
Optional shop header Many endpoints are additionally scoped to a shop (point of sale / storefront). Pass the shop ULID to restrict results to that shop:

5. Multi-Tenancy

The Genuka API is multi-tenant. Each company is an isolated tenant. All data — products, orders, customers, payments, etc. — is scoped to the company identified by X-Company. How it works
  • The X-Company header value is read by the CheckCurrentCompany middleware and injected as companyId into the request context.
  • Eloquent global scopes (via CompanyScope) automatically append WHERE company_id = ? to every query for scoped models.
  • The owner middleware (applied to the /admin route group) additionally verifies that the authenticated user belongs to that company. If the user’s company does not match X-Company, the request is rejected with HTTP 401.
Missing or wrong company

6. Pagination

All paginated list endpoints return the same JSON envelope. The API does not use the default Laravel data/links/meta wrapper. It uses a custom pagination object:

Pagination query parameters

Example
Some endpoints also support simple_paginate=1 which returns a cursor-based response without a total count (faster on very large tables).
The API uses Spatie Laravel Query Builder conventions. Parameters are namespaced with brackets.

Filtering

Examples:
Multiple filters can be combined. Refer to each resource’s documentation for its allowed filter names.

Sorting

Examples:

Including relationships

Example:
Only whitelisted includes are accepted. Requesting an unknown include returns a 400 error.

Field selection

Restricts the response to the listed fields, reducing payload size.

Date range filtering

Many index endpoints accept startDate and endDate parameters (ISO 8601 format):

8. Error Handling

The API returns a consistent JSON error envelope for all error responses.

Error envelope shape

For validation errors (HTTP 422), Laravel includes an additional errors object with per-field messages:

HTTP status codes

401 example
403 example (invalid OAuth scope)
422 example (missing company header)
500 example

9. Data Formats

Resource IDs — ULIDs

All resource identifiers are ULIDs — 26-character alphanumeric strings that are lexicographically sortable by creation time.
ULIDs are case-insensitive but are stored and returned in lowercase.

Timestamps

All timestamps are returned in ISO 8601 format, UTC:

Money / Prices

Monetary amounts (prices, order totals, payment amounts) are stored and returned as decimal numbers (floating point, two decimal places). There is no integer-cent encoding.
Currency is not embedded on individual resources. The company’s configured currency is available on the company object:

10. Rate Limiting

The api middleware group applies throttling to all /2023-11 routes via Laravel’s rate limiter. When the limit is exceeded, the server responds with HTTP 429. Laravel includes standard rate-limit headers in responses: