Farther ShoreDocs
Go to Farther Shore
Understand gateway behavior
Consuming the API
Send the subscriber keyHandle a denial as dataUpstream responses
Monetary admission
Usage limits
Gate API routes by plan
Diagnose a denied request
Response & deny codes
gateway HTTP contracts
Status
Docs/Connect your application/Consuming the API

Consuming the API

Call a Farther Shore gateway route with a subscriber key and handle stable denials.

Subscribers call the Farther Shore business gateway, not your direct backend origin. The gateway resolves the subscriber, plan, environment, route, limits, and backend, then signs and forwards an admitted request.

Send the subscriber key

The default business API-key header is x-api-key. If the business contract sets a different authHeader, use that exact header instead.

ts
async function createJob(apiKey: string, input: unknown) {
  const response = await fetch("https://<business-gateway-host>/v1/jobs", {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-api-key": apiKey,
    },
    body: JSON.stringify(input),
  });

  if (response.ok) return response.json();

  const body = await response.json().catch(() => ({}));
  throw new BusinessApiError(response.status, body);
}

Use a test key and preview gateway for preview environments and a live key for Main. Do not send a runtime token, CLI credential, or provider secret to a business route.

Handle a denial as data

Denied requests return a stable machine-readable code. Usage-limit denials also include an _fs envelope that tells a client what kind of limit fired and what reaction is appropriate:

json
{
  "error": "Too many requests in flight.",
  "code": "concurrency_limit_exceeded",
  "_fs": {
    "limitClass": "concurrency",
    "reaction": "queue",
    "limitOrigin": "platform",
    "retrySafe": true,
    "mustModify": false,
    "decisionId": "dec_8a91…",
    "requestId": "req_2c7f…",
    "envelopeVersion": 1
  }
}

Branch on _fs.limitClass, _fs.reaction, retrySafe, and mustModify; do not infer the remedy only from the HTTP status.

Limit classTypical response
rate, concurrency, adaptiveHonor Retry-After; retry the same request only when retrySafe is true
capacityReduce or change the request before retrying
quota, spendUpgrade, top up, or wait for the applicable reset; blind retries do not help
ts
async function handleDeny(response: Response) {
  const body = await response.json().catch(() => ({}));
  const detail = body._fs;

  if (!detail) throw new Error(body.code ?? `HTTP ${response.status}`);

  if (detail.retrySafe) {
    const seconds = Number(response.headers.get("retry-after") ?? 1);
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
    return { retry: true };
  }

  if (detail.reaction === "upgrade") return { upgrade: true };
  if (detail.mustModify) return { modifyRequest: true };
  return { code: body.code, decisionId: detail.decisionId };
}

Always bound retries, add jitter, and preserve the operation's own idempotency key when retrying a write.

Upstream responses

An admitted request normally returns your backend's response status, body, and safe headers. Farther Shore consumes internal signing and metering headers; they are not an application API. A 503 origin_unavailable means the selected environment has no usable backend binding or tunnel connection. It is an operator problem, not a subscriber authentication failure.

Use the denial requestId or decisionId when correlating a subscriber report with platform and backend diagnostics. Never include API keys or runtime tokens in logs.

PreviousUnderstand gateway behaviorNextMonetary admission

On this page

Send the subscriber keyHandle a denial as dataUpstream responses