Starship Rewards API

Request Headers

Headers required and optional on every Starship API request — auth, HMAC signing, idempotency, and content negotiation

Request Headers

This page lists every header the Starship API recognizes on client (/api/v1/*) requests, when each one is required, and how to construct the signing and idempotency headers correctly.

At a Glance

HeaderRequired onPurpose
X-API-KeyAll /api/v1/* callsYour public API key.
X-API-SecretAll /api/v1/* callsYour paired API secret. Sent over TLS only.
X-SignatureCredentials with HMAC enabledHMAC-SHA256 signature of the request. Format: t=<unix_ts>,v1=<hex_hmac>.
Idempotency-KeyPOST /orders, POST /cart/checkout, POST /payouts8–256-char client-generated key that makes retries safe.
Content-TypeAll POST/PUT/PATCHapplication/json unless uploading files.
AcceptOptionalDefault application/json.
X-Request-IdOptionalYour trace ID — echoed back in responses for cross-log correlation.
Retry-AfterResponse-onlyServer sends this on 429; honor its value.

Authentication Headers

X-API-Key + X-API-Secret

The primary authentication mechanism. Your key pair is provisioned through the Admin console and must be rotated on any suspected compromise.

curl https://api.starshiprewards.com/api/v1/products \
  -H 'X-API-Key: sk_live_abc123...' \
  -H 'X-API-Secret: ***never-log-this***'

Never put X-API-Secret in client-side code, version control, or logs. Server-to-server only. If secret is exposed, rotate immediately via the Admin console.

JWT Bearer tokens are no longer supported on the public /api/v1/* API. All client integrations must use X-API-Key + X-API-Secret.

HMAC Signature — X-Signature

HMAC request signing is an optional per-credential control. When your credential has HMAC enabled, every /api/v1/* request must carry a valid signature or the server returns 401 E_UNAUTHORIZED_ACCESS. Credentials without HMAC enabled proceed with just the key/secret check. Ask your Starship contact to enable HMAC on your credential for production.

See the HMAC Signing guide for signing-secret provisioning and test vectors.

Header Format

X-Signature: t=<unix_timestamp>,v1=<hex_hmac_sha256>
  • t — Unix seconds when the signature was generated. Must be within ±5 minutes of server time (300 s drift window), otherwise the request is rejected with 401 E_UNAUTHORIZED_ACCESS: request timestamp expired.
  • v1 — Lowercase hex-encoded HMAC-SHA256 of the canonical string using your signing secret as the key.

The v1 prefix is a version tag — if Starship ever adopts a new signing scheme, older clients using v1 continue to work during the deprecation window.

Canonical String — five lines

The string you HMAC is exactly five lines joined by \n, in this order:

METHOD
PATH
SORTED_QUERY
SHA256_HEX(body)
TIMESTAMP
  • METHOD — uppercase HTTP verb (GET, POST, PUT, PATCH, DELETE).
  • PATH — request path without the query string (e.g., /api/v1/products, not /api/v1/products?limit=50).
  • SORTED_QUERY — query parameters sorted alphabetically by key, joined as k=v&k=v. Empty string if there are no query params. Repeated keys keep their relative order within the key group.
  • SHA256_HEX(body) — lowercase hex SHA-256 of the raw request body. For requests with no body (e.g., GET), hash the empty string: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
  • TIMESTAMP — the same integer you put in the t= parameter of the header.

Common footgun. The path does not include query string — the query is line 3, separately, and must be sorted alphabetically by key. Order of how your HTTP client assembled the URL does not matter. If you sign ?b=2&a=1 but the server parses a=1&b=2, you will mismatch unless you sort.

Minimal Example (bash + openssl + curl)

Compute the signature in the shell and pass it straight to curl. Substitute your own SIGNING_SECRET, method, path, query, and body. Language-specific reference implementations will be published later.

#!/usr/bin/env bash
set -euo pipefail

SIGNING_SECRET="your_signing_secret"
METHOD="GET"
PATH_ONLY="/api/v1/products"         # no query string here
QUERY="category_id=1&per_page=50"    # sorted alphabetically by key
BODY=""                              # request body, empty for GET

# Line 1: METHOD
# Line 2: PATH (no query)
# Line 3: SORTED_QUERY
# Line 4: SHA256_HEX(body)
# Line 5: TIMESTAMP
TS="$(date -u +%s)"
BODY_HASH="$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $2}')"
CANONICAL="$(printf '%s\n%s\n%s\n%s\n%s' "$METHOD" "$PATH_ONLY" "$QUERY" "$BODY_HASH" "$TS")"
HMAC="$(printf '%s' "$CANONICAL" | openssl dgst -sha256 -hmac "$SIGNING_SECRET" -hex | awk '{print $2}')"
SIG="t=${TS},v1=${HMAC}"

# Send the request — note the query string IS on the URL even though it's
# on its own line in the canonical string. Only signing treats them separately.
curl -sS "https://api.starshiprewards.com${PATH_ONLY}?${QUERY}" \
  -H "X-API-Key: ${API_KEY}" \
  -H "X-API-Secret: ${API_SECRET}" \
  -H "X-Signature: ${SIG}"

Why openssl not hmac256 or similar. openssl dgst ships on macOS, Linux, and most CI images out of the box — no install step. For production code, use your language's native crypto library; this shell example is for quick testing and copy-paste reproduction against a live API.

Idempotency-Key

Required on any endpoint that creates a financial obligation — orders, cart checkouts, payouts. Prevents duplicate charges when a network error leaves you uncertain whether your request succeeded.

Rules

PropertyConstraint
Length8–256 characters
CharactersAny printable ASCII
UniquenessUnique per request body — retrying with the same key returns the cached response
WindowKeys are remembered for 24 hours, then may be reused

Good Key Formats

ord_<your_internal_order_id>_<unix_ts>
checkout_<user_id>_<cart_hash>
payout_<request_uuid>

What the Server Does

  1. First request with a key — processed normally. Response is cached.
  2. Retry with same key + same body — returns cached response. No side effects.
  3. Retry with same key + different body — returns 409 RESOURCE_CONFLICT. Fix your client; this almost always means two independent operations are reusing the same key.
  4. Key older than 24h — treated as a fresh request.

See the full Idempotency Guide for retry patterns.

Content Negotiation

Content-Type

Required on all requests with a body.

Content-Type: application/json; charset=utf-8

Starship does not currently accept form-encoded bodies on /api/v1/*. charset=utf-8 is optional — UTF-8 is assumed.

Accept

Optional. Defaults to application/json. No other media types are supported today; reserved for future use (e.g., CSV exports).

Tracing & Observability

X-Request-Id

Optional on requests. If you send one, Starship echoes it on the response and includes it in server-side logs — making cross-log correlation trivial when you need support to trace an issue.

X-Request-Id: 01HQCA5V7Z8K2MNPQRTXYW9J3B

If you do not send one, the server generates one and returns it in X-Request-Id on the response.

User-Agent

Not required, but strongly recommended. A specific UA helps support identify which of your integrations is making a request.

User-Agent: acme-gift-platform/2.4.1 (+https://acme.example)

Response Headers You Should Know

HeaderWhen presentPurpose
X-Request-IdAlwaysTrace ID (echoed from request or newly generated).
Retry-After429 / 503 responsesSeconds (or HTTP-date) to wait before retrying.
X-Page / X-Per-PagePaginated GET responsesCurrent page + items per page.
X-Total-Count / X-Total-PagesPaginated GET responsesTotal records / pages for the current filter set.
X-Has-MorePaginated GET responsestrue/false convenience for infinite scroll.

Forbidden Header

X-Automation-Request

Used internally to mark automated test traffic. Starship blocks production write traffic that carries this header. If your client accidentally injects it, you will see 403 FORBIDDEN_ACTION on every POST/PUT/PATCH to /api/v1/* in production. Remove it.

Next Steps