Examples

From gateway logs to a findings report

Copyable cURL and local Node.js sidecar examples.

Security boundary

PayAPIKey's hosted endpoints do not accept gateway administrator tokens, channel keys, cookies, or sessions. Native API reads must happen in a local connector/sidecar you control; the hosted service receives stripped billing metadata only.

Reconcile canonical data directly

Shell
curl -s https://www.payapikey.com/api/v1/examples/reconciliation \
  | jq '.data' > reconciliation.json

curl -s -X POST https://www.payapikey.com/api/v1/reconcile \
  -H 'Content-Type: application/json' \
  --data-binary @reconciliation.json \
  | jq '.data.summary, .data.findings'

Read New API locally, then call the hosted API

This script reads the native API on your machine. The administrator token is sent only to NEW_API_URL and never enters the PayAPIKey request body.

connector.mjs
const gatewayUrl = process.env.NEW_API_URL;
const statusResponse = await fetch(new URL("/api/status", gatewayUrl));
const gatewayResponse = await fetch(
  new URL("/api/log/?type=2&p=1&page_size=100", gatewayUrl),
  {
    headers: {
      Authorization: `Bearer ${process.env.NEW_API_ACCESS_TOKEN}`,
      "New-Api-User": process.env.NEW_API_USER_ID,
    },
  },
);

if (!statusResponse.ok || !gatewayResponse.ok) throw new Error("Gateway read failed");
const status = await statusResponse.json();
const quotaPerUnit = Number(status?.data?.quota_per_unit);
if (!(quotaPerUnit > 0)) throw new Error("Missing quota_per_unit");

const privateFields = new Set([
  "key", "secret", "api_key", "access_token", "authorization",
  "password", "cookie", "session", "prompt", "completion",
  "content", "messages", "request_body", "response_body",
]);
function stripPrivate(value) {
  if (Array.isArray(value)) return value.map(stripPrivate);
  if (!value || typeof value !== "object") return value;
  return Object.fromEntries(
    Object.entries(value)
      .filter(([key]) => !privateFields.has(key.toLowerCase()))
      .map(([key, child]) => [key, stripPrivate(child)]),
  );
}
const logs = stripPrivate(await gatewayResponse.json());

// Submit billing metadata only. Never add credentials, prompts, or completions.
const normalizedResponse = await fetch(
  "https://www.payapikey.com/api/v1/normalize",
  {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      connector: "new-api", currency: "USD", quotaPerUnit, logs,
    }),
  },
);

if (!normalizedResponse.ok) throw new Error(await normalizedResponse.text());
const { data: canonical } = await normalizedResponse.json();

const reportResponse = await fetch("https://www.payapikey.com/api/v1/reconcile", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(canonical),
});

if (!reportResponse.ok) throw new Error(await reportResponse.text());
const { data: report } = await reportResponse.json();
console.log(report.summary);
console.table(report.findings);
Shell
NEW_API_URL='https://gateway.example.com' \
NEW_API_ACCESS_TOKEN='replace-locally' \
NEW_API_USER_ID='1' \
node connector.mjs

One API and one-hub use the same pattern: change connector to one-api or one-hub and verify the compatibility path against the deployed version first.

Add upstream cost and payment funding

The normalization endpoint converts downstream log fields. Build upstream provider charges and payment-provider settlements from their respective exports, then merge them into the canonical object.

JavaScript
canonical.upstreamCharges = upstreamExport.map(mapUpstreamCharge);
canonical.payments = processorExport.map(mapPaymentSettlement);

// Payment captures fund the wallet; they are not added to consumption revenue.
const response = await fetch("https://www.payapikey.com/api/v1/reconcile", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(canonical),
});

VoAPI aggregate reads and file import

No VoAPI request-level live-read contract is claimed

VoAPI v2 /api/dash/statistics can be read locally for aggregate totals, but it cannot support Request ID findings. For exact reconciliation, export billing metadata and import downstream, upstream, or payment CSV/JSON separately. Do not treat a One API compatibility path as a VoAPI v2 endpoint.

Minimum downstream CSV columns: requestId, occurredAt, model, status, chargedAmount. Minimum upstream columns: occurredAt, model, status, costAmount; requestId is recommended. Minimum payment columns: transactionId, occurredAt, status, amount.

Open the browser workbench