Loading theme toggle

Storefront BFF API

The Storefront BFF API is a REST API that provides a stable, domain-oriented interface for building customer experiences on the Horizon Storefront.

The API uses predictable resource-oriented URLs, accepts JSON request bodies, returns JSON responses, and uses standard HTTP response codes and authentication.

Resource groups

Endpoints are organised by domain:

  • Authentication - /auth/login, /auth/logout, /auth/token
  • Products - product detail, variants, media
  • Categories - navigation, merchandising, collections
  • Cart - cart lifecycle, line items, promotions
  • Orders - order detail and history
  • Customers - profiles, addresses, preferences
  • Content - CMS-driven marketing content
  • Search - search suggestions and queries
  • Health - readiness and liveness endpoints

Just getting started?

Check out our development quickstart guide.

Base URL

All API requests should be made to this base URL. See Environments for staging and development URLs.

https://storefront.api.templeandwebster.com

Authentication

The Storefront API uses different authentication methods depending on the caller type.

Auth schemes

SchemeHeaderFormatUsed for
BFFTokenX-BFF-TokenJWTCustomer sessions
BearerAuthAuthorizationJWTService-to-service

Public endpoints

Public endpoints require no authentication. These include:

  • Product and category browse
  • Search suggestions
  • CMS content and static pages

These endpoints do not expose PII and can be cached at the edge.

Customer authentication

Customer flows (cart, checkout, orders, profile) require a BFF session token obtained via login.

Login flow

  1. Call POST /auth/login with credentials and reCAPTCHA response
  2. Receive accessToken, tokenType, expiresIn, and customer object
  3. Include the token in X-BFF-Token header on subsequent requests
curl -X POST https://storefront.api.templeandwebster.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "emailAddress": "customer@example.com",
    "password": "SecurePassword123!",
    "gRecaptchaResponse": "03AGdBq27..."
  }'

Response

The login response contains:

accessTokenstringrequired

JWT token to include in subsequent requests.

tokenTypestringrequired

Token type, always "Bearer".

expiresInintegerrequired

Token validity in seconds.

customerobjectrequired

Customer profile object with id, emailAddress, firstName, lastName.

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "tokenType": "Bearer",
  "expiresIn": 3600,
  "customer": {
    "id": "cust-123",
    "emailAddress": "customer@example.com",
    "firstName": "Jane",
    "lastName": "Smith"
  }
}

Authenticated request

Include the token in the X-BFF-Token header for all authenticated requests.

curl https://storefront.api.templeandwebster.com/customers/me \
  -H "X-BFF-Token: eyJhbGciOiJIUzI1NiIs..."

Token lifetime

  • expiresIn indicates token validity in seconds
  • 401 Unauthorized indicates an expired or invalid token
  • 403 Forbidden indicates insufficient permissions

Tokens are short-lived. Handle expiry by re-authenticating or using the refresh flow at POST /auth/token.

Server-to-server

Internal services use Google Cloud identity tokens.

These endpoints must not be exposed to the public internet.

curl https://storefront.api.templeandwebster.com/internal/endpoint \
  -H "Authorization: Bearer ya29.c.Kp8B..."

Headers summary

HeaderRequiredDescription
X-BFF-TokenCustomer flowsCustomer session JWT
AuthorizationS2S callsGCP identity token
Correlation-IdRecommendedRequest tracing ID (UUID)

Errors

The Storefront BFF uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided (e.g., a required parameter was omitted, a validation failed, etc.). Codes in the 5xx range indicate an error with the server (these should be rare).

Some 4xx errors that could be handled programmatically include an error code that briefly explains the error reported.

Attributes

codeintegerrequired

HTTP status code (e.g., 400, 401, 404, 422, 429, 500).

messagestringrequired

A human-readable message providing more details about the error. These messages can be shown to your users or logged for debugging.

codestring

For some errors that could be handled programmatically, a short string indicating the error code reported.

errorsarray

For validation errors, a list of detailed field-level errors. Each error contains field, code, and message properties.

HTTP Status Code Summary

StatusNameDescription
200OKEverything worked as expected.
201CreatedResource was successfully created.
204No ContentRequest succeeded with no response body.
400Bad RequestThe request was malformed or semantically invalid.
401UnauthorizedNo valid authentication credentials provided.
403ForbiddenThe authenticated user doesn't have permissions.
404Not FoundThe requested resource doesn't exist.
409ConflictThe request conflicts with the current state.
422Unprocessable EntityValidation failed for one or more fields.
429Too Many RequestsToo many requests hit the API too quickly.
500Internal Server ErrorSomething went wrong on the server's end.
502Bad GatewayA downstream service returned an invalid response.
503Service UnavailableA downstream service is temporarily unavailable.
504Gateway TimeoutA downstream service did not respond in time.

Detailed Error

Within the errors array, each detailed error contains:

fieldstring

If the error is field-specific, the field path related to the error. For example, you can use this to display a message near the correct form field (e.g., email, shippingAddress.postcode).

codestring

Machine-readable error code for this specific field error (e.g., REQUIRED, INVALID_FORMAT, OUT_OF_RANGE).

messagestring

Human-readable explanation for this field error.

Error Types

The type field in the error response indicates the category of error.

TypeDescription
api_errorAPI errors cover any other type of problem, and are extremely uncommon.
auth_errorAuthentication errors occur when credentials are missing, invalid, or expired.
validation_errorValidation errors arise when your request has invalid parameters.
not_found_errorNot found errors occur when the requested resource doesn't exist.
conflict_errorConflict errors occur when the request conflicts with current state.
rate_limit_errorRate limit errors occur when too many requests hit the API too quickly.

Requests

The Storefront BFF is a JSON-over-HTTP API. This page covers the common patterns used across endpoints.

HTTP methods

MethodUseIdempotent
GETRead a resource or collectionYes
POSTCreate or perform an actionNo
PUTReplace or set resource stateYes
DELETERemove a resourceYes

Required headers

Any request with a body must set Content-Type: application/json.

Content-Type: application/json
Accept: application/json

Resource IDs

Resources have stable, unique identifiers. Treat IDs as opaque strings.

{
  "id": "cust-660e9511-f3ac-42e5-b827-6f7g9b8e0c4d",
  "version": 3,
  "createdAt": "2024-02-10T14:20:00Z",
  "lastModifiedAt": "2024-11-24T09:30:00Z"
}

Money

Monetary values use minor units (cents).

Do not send floating-point dollar values. Always use the structured Money object.

typestringrequired

Precision type, always "centPrecision".

currencyCodestringrequired

ISO 4217 currency code (e.g., AUD, NZD).

centAmountintegerrequired

Amount in minor units (cents).

fractionDigitsintegerrequired

Number of decimal places for the currency.

{
  "type": "centPrecision",
  "currencyCode": "AUD",
  "centAmount": 1999,
  "fractionDigits": 2
}

Localised strings

Text that varies by locale uses LocalizedString.

Keys are IETF language tags (e.g., en-AU).

{
  "en-AU": "Premium 3 Seater Sofa Bed",
  "en-NZ": "Premium 3 Seater Sofa Bed"
}

Timestamps

All timestamps use RFC 3339 / ISO 8601 format in UTC.

Always include the trailing Z for UTC. Do not send local time zone offsets.

{
  "createdAt": "2024-02-10T14:20:00Z",
  "lastModifiedAt": "2024-11-24T09:30:00Z"
}

Paged responses

Collection endpoints return a paged envelope.

See Pagination for details.

limitintegerrequired

Maximum number of results requested.

offsetintegerrequired

Number of results to skip.

countintegerrequired

Number of results in this response.

totalintegerrequired

Total number of results available.

resultsarrayrequired

Array of result objects.

{
  "limit": 20,
  "offset": 0,
  "count": 20,
  "total": 350,
  "results": [...]
}

Error responses

All errors use a standard structure.

See Errors for the full list of status codes and error types.

{
  "status": 422,
  "message": "Validation error",
  "code": "VALIDATION_ERROR",
  "errors": [
    {
      "field": "email",
      "code": "REQUIRED",
      "message": "Field is required"
    }
  ]
}

Environments

The Storefront API runs in multiple environments with the same contract but different base URLs.

Base URLs

EnvironmentBase URL
Productionhttps://storefront.api.templeandwebster.com
Staginghttps://storefront.staging.api.templeandwebster.com
Developmenthttps://storefront.dev.api.templeandwebster.com
Localhttp://localhost:8080

Production

Live customer traffic with real data. Subject to SLOs, monitoring, and change controls.

Use for:

  • Validating behaviour with feature flags after deployment
  • Investigating production issues with appropriate access

Never run synthetic load tests or use fake customer data against production.

https://storefront.api.templeandwebster.com

Staging

Mirrors production topology. Used for pre-release verification and regression testing.

Use for:

  • End-to-end test scenarios
  • QA and UAT flows
  • Verifying migrations and contract changes

Data may be synthetic or sanitised production records.

https://storefront.staging.api.templeandwebster.com

Development

Shared integration environment for feature development.

Use for:

  • Testing API changes from feature branches
  • Manual testing from local frontend instances
  • Early integration testing with other teams

Data and configuration may change frequently.

https://storefront.dev.api.templeandwebster.com

Local

Runs the BFF on http://localhost:8080.

Typical setup:

  • Storefront Next on http://localhost:3000
  • Frontend proxies API calls to localhost:8080

See the storefront-bff README for setup details.

http://localhost:8080

Choosing an environment

TaskEnvironment
Building a featureLocal, then Development
Cross-team integrationDevelopment, then Staging
Preparing a releaseStaging
Investigating issuesStaging or Development first

Pagination

All top-level API resources have support for bulk fetches through "list" API methods. These list API methods share a common structure and accept, at a minimum, the following parameters: limit and offset.

The Storefront BFF uses offset-based pagination through the limit and offset parameters.

Parameters

limitinteger

This specifies a limit on the number of objects to return, ranging between 1 and 100. Default is 20.

offsetinteger

The number of results to skip before returning results. Use this to page through results. Default is 0.

GET /products?limit=20&offset=40

List Response Format

limitintegerrequired

The limit that was applied to this request.

offsetintegerrequired

The offset that was applied to this request.

countintegerrequired

The number of results in this response (may be less than limit on the last page).

totalintegerrequired

The total number of results available across all pages.

resultsarrayrequired

An array containing the actual response elements.

{
  "limit": 20,
  "offset": 40,
  "count": 20,
  "total": 350,
  "results": [
    {
      "id": "prod-12345",
      "name": "Premium Sofa",
      "price": {
        "centAmount": 199900,
        "currencyCode": "AUD"
      }
    }
  ]
}

Pagination example

To paginate through results:

  1. Make an initial request with your desired limit
  2. Check if offset + count < total to determine if more pages exist
  3. Increment offset by limit for the next page
  4. Repeat until you've retrieved all results
# First page
curl "https://storefront.api.templeandwebster.com/products?limit=20&offset=0"

# Second page
curl "https://storefront.api.templeandwebster.com/products?limit=20&offset=20"

# Third page
curl "https://storefront.api.templeandwebster.com/products?limit=20&offset=40"

Best practices

  • Use reasonable page sizes (20-50 items) for optimal performance
  • Cache results where appropriate to reduce API calls
  • Handle the case where total may change between requests (items added/removed)
  • Consider using filtering to reduce result sets before pagination

Filtering

The Storefront BFF provides several filtering mechanisms for collection endpoints.

Query parameters

Many endpoints expose explicit parameters for common filters:

  • Boolean flags: inStock, featured, isDefault
  • Value ranges: minPrice, maxPrice (in cents)
  • Enumerations: status=active|inactive|discontinued
  • Identifiers: category, brand, customerId
curl "https://storefront.api.templeandwebster.com/products?category=sofas&status=active&minPrice=50000&maxPrice=200000"

Full-text search

Endpoints with search use the query parameter.

The query is passed to the search system and may support stemming, synonyms, and fuzzy matching.

curl "https://storefront.api.templeandwebster.com/search?query=sofa%20bed"

Structured filters

For complex conditions, use the filter parameter with operators.

Operators

OperatorDescription
[eq]Equal (default)
[ne]Not equal
[gt]Greater than
[gte]Greater than or equal
[lt]Less than
[lte]Less than or equal
curl "https://storefront.api.templeandwebster.com/products?filter[status]=active&filter[price[gte]]=5000&filter[price[lte]]=20000"

Filter examples

Combine multiple filter parameters in a single request.

# Active products between $50 and $200
GET /products?filter[status]=active&filter[price[gte]]=5000&filter[price[lte]]=20000

# Orders created after a date
GET /orders?filter[createdAt[gte]]=2025-01-01T00:00:00Z

Locale filtering

Content endpoints support a locale parameter.

Pattern: ^[a-z]{2}-[A-Z]{2}$ (e.g., en-AU). Default: en-AU.

curl "https://storefront.api.templeandwebster.com/content/footer?locale=en-AU"

Error handling

Invalid filters return 400 Bad Request with details about the invalid parameter.

{
  "status": 400,
  "message": "Invalid filter parameter",
  "code": "INVALID_FILTER",
  "errors": [
    {
      "field": "filter[price[gte]]",
      "code": "INVALID_VALUE",
      "message": "Expected a numeric value"
    }
  ]
}

Headers

Standard HTTP headers used by the Storefront BFF.

Request headers

HeaderRequiredDescription
Content-TypeYes (with body)Must be application/json
AcceptRecommendedShould be application/json
X-BFF-TokenCustomer endpointsCustomer session JWT
AuthorizationS2S endpointsBearer <gcp-identity-token>
X-Api-VersionYesAPI version (e.g., v1.0)
Correlation-IdRecommendedUUID for request tracing

Content-Type

Required for any request with a body.

If missing or incorrect, returns 415 Unsupported Media Type.

Content-Type: application/json

X-BFF-Token

Customer session token from the login flow.

Required for customer endpoints (cart, orders, profile). See Authentication.

X-BFF-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Authorization

Google Cloud identity token for service-to-service calls.

Used by internal services and batch jobs.

Authorization: Bearer ya29.c.Kp8B...

X-Api-Version

Selects the resource schema version.

Pattern: v{major}.{minor}. Required on requests, echoed in responses.

X-Api-Version: v1.0

Correlation-Id

UUID for correlating logs and traces across services.

Optional. If not provided, the BFF generates one. Reuse the same ID for related requests in a single user flow (e.g., checkout).

Correlation-Id: 550e8400-e29b-41d4-a716-446655440000

Response headers

HeaderDescription
Content-Typeapplication/json
X-Api-VersionVersion used for this response
Correlation-IdRequest correlation ID

Sorting

Collection endpoints support sorting via the sort query parameter.

Syntax

Direction must be asc (ascending) or desc (descending).

sort={field}:{direction}

Examples

Sort by a single field with ascending or descending direction.

curl "https://storefront.api.templeandwebster.com/products?sort=name:asc" curl "https://storefront.api.templeandwebster.com/orders?sort=createdAt:desc"

Multi-field sorting

Repeat the sort parameter for multiple fields.

Sort is applied in parameter order:

  1. Featured products first
  2. Then by popularity
  3. Then alphabetically by name
curl "https://storefront.api.templeandwebster.com/products?sort=featured:desc&sort=popularity:desc&sort=name:asc"

Common sort fields

EndpointTypical fields
GET /productsname, price, createdAt, popularity, featured
GET /orderscreatedAt, status, totalAmount
GET /categoriesname, position

Error handling

Unsupported sort fields return 400 Bad Request.

{ "status": 400, "message": "Unsupported sort field", "code": "INVALID_SORT_FIELD", "errors": [ { "field": "sort", "code": "UNSUPPORTED", "message": "The sort field 'foo' is not supported" } ] }

With pagination

Sorting is applied before pagination. Always use the same sort parameters when paginating to maintain stable ordering.

curl "https://storefront.api.templeandwebster.com/products?sort=price:asc&limit=20&offset=20"

Idempotency

The API supports idempotency for safely retrying requests without performing the same operation twice.

HTTP method semantics

MethodIdempotentNotes
GETYesNo side effects
HEADYesNo side effects
PUTYesSets resource state
DELETEYesRemoves resource
POSTNoMay create duplicates without care

Safe to retry

These operations can be retried on network failures or 5xx errors:

  • GET, HEAD, OPTIONS - always safe
  • PUT, DELETE - idempotent by design
  • POST - only where explicitly documented

Good: state-setting

Prefer state-setting operations over action-style operations.

The client sends the desired state. Retries produce the same outcome.

PUT /carts/{cartId}
Content-Type: application/json

{
  "discountCode": "WELCOME10"
}

Risky: action-style

Action endpoints are harder to make safe. Prefer state-setting alternatives.

# Action-style (harder to make safe)
POST /orders/{orderId}/cancel

# Better: state-setting alternative
PUT /orders/{orderId}
Content-Type: application/json

{
  "status": "CANCELLED"
}

Idempotent actions

When actions are necessary, implement them to be retry-safe:

Server behaviour:

  • If confirmable, confirm and return the result
  • If already confirmed, return the same result (not an error)
  • If in an incompatible state, return 4xx
POST /checkout/{sessionId}/confirm

Idempotency keys

The API does not currently require explicit idempotency keys. If introduced in future, they will:

  • Use an Idempotency-Key header
  • Be scoped to method, path, and principal
  • Store and replay original responses for matching requests

Retry guidance

ScenarioSafe to retry?
Network timeoutYes for idempotent methods
5xx responseYes with exponential backoff
4xx responseNo - client error, fix the request
Non-idempotent POSTOnly if documented as safe

Rate Limits

The API applies rate limiting to protect downstream systems and ensure consistent performance.

429 Too Many Requests

When you exceed a rate limit, the API returns a 429 status code.

{ "status": 429, "message": "Too many requests, please try again later", "code": "RATE_LIMIT_EXCEEDED" }

Retry-After header

The response may include a Retry-After header indicating how many seconds to wait before retrying.

Retry-After: 5

Limit scopes

Rate limits may be applied by:

  • IP address - protects against abusive traffic
  • Customer session - prevents UI loops
  • Service account - for server-to-server integrations
  • Endpoint - expensive operations (e.g., search) may have stricter limits

Exact thresholds are environment-specific and may change without notice.

Browser clients

  • Debounce search and autocomplete requests
  • Cache static data lookups
  • On 429, wait per Retry-After and show a friendly message
  • Don't retry aggressively

Server clients

  • Implement exponential backoff with jitter: 1s, 2s, 4s, 8s (capped)
  • Reuse HTTP clients with connection pooling
  • Stagger retries after outages to avoid thundering herd

Example backoff

Implement exponential backoff with jitter for production clients.

async function fetchWithRetry(url, options, maxRetries = 3) { for (let attempt = 0; attempt <= maxRetries; attempt++) { const response = await fetch(url, options); if (response.status === 429) { const retryAfter = response.headers.get('Retry-After') || 1; const delay = Math.min(Math.pow(2, attempt) * 1000, 30000); await sleep(Math.max(retryAfter * 1000, delay)); continue; } return response; } throw new Error('Max retries exceeded'); }

Non-production environments

Rate limits in development and staging may be less strict but can still apply to expensive endpoints like search.

Design your clients to handle 429 gracefully in all environments.

Versioning

The API uses versioning to evolve without breaking existing integrations.

Base URL

The base URL is versionless. Versioning is applied at the resource level or via headers.

https://storefront.api.templeandwebster.com/products

Resource versioning

Individual resources can include a version suffix.

Version format: v{major} or v{major}.{minor}.

Omitting a version returns the latest supported version for that resource.

/orders/v1 /orders/v2 /customers/{id}/orders/v1

Header versioning

The X-Api-Version header controls cross-cutting behaviour.

Resource URL versions take precedence over the header for resource-specific behaviour.

X-Api-Version: v1.0

Breaking changes

These require a new major version:

  • Removing fields
  • Changing field types or formats
  • Changing field semantics
  • Changing status code behaviour

These are backwards-compatible and don't require a new version:

  • Adding new optional fields
  • Adding new endpoints
  • Adding new parameters with defaults

Version lifecycle

  1. Introduce v1 - Resource becomes generally available
  2. Add v1.x - Optional improvements requiring explicit opt-in
  3. Introduce v2 - Breaking changes; v1 enters deprecation window
  4. Deprecate v1 - Removed after the agreed period

We limit live major versions to two at a time.

Client guidance

  • Default to unversioned URLs unless you need a specific version
  • For stable integrations, use explicit versions (e.g., /orders/v1)
  • Track deprecation notices and plan upgrades
  • Test across environments before production deployment