195 countries · 217 jurisdictions · SOC 2 readiness roadmap

Developer Portal

Build on KeyBS Trust Intelligence

The KeyBS API lives on its own subdomain — api.trust.keybs.io, versioned under /v1. This portal is the canonical reference for endpoints, authentication, webhooks, the badge embed, errors, and rate limits.

Overview

Production API base
https://api.trust.keybs.io/v1
Internal app mirror
https://app.trust.keybs.io/api/public/v1/*
Developer portal
https://developers.trust.keybs.io
Public verify URL
https://verify.keybs.io/r/<verificationId>
Marketing site
https://trust.keybs.io

Every endpoint returns JSON with a stable error envelope and an X-Request-Id header on every response. Include the request ID in any support correspondence.

Authentication

Authenticate every request with a tenant-scoped API key issued from the client portal → API keys. Keys are hashed at rest and shown exactly once on creation — store them in your secrets manager.

  • Authorization: Bearer kbs_live_… (production)
  • Authorization: Bearer kbs_test_… (sandbox)
  • Each key carries a fixed scope set; insufficient scope returns 403 invalid_scope.
  • Revoked keys are rejected immediately — no caching window.

Supported scopes

  • verification:create
  • verification:read
  • document:create
  • report:read
  • webhook:manage

Endpoints

POSThttps://api.trust.keybs.io/v1/verificationsverification:create

Create a new verification case.

Request

{
  "supplier_name": "Acme Trading Co.",
  "country_code": "IN",
  "tier": "tier2",
  "package": "standard",
  "contact_email": "ops@acme.example",
  "client_reference": "PO-2026-0042"
}

Response

{
  "id": "f7e9…",
  "verification_id": "KBS-VR-7F3A",
  "status": "submitted",
  "supplier": { "name": "Acme Trading Co.", "country_code": "IN", "tier": "tier2" },
  "created_at": "2026-05-31T10:21:00Z"
}
GEThttps://api.trust.keybs.io/v1/verificationsverification:read

List tenant-owned verification cases. Supports ?status= and ?limit= (max 200).

Response

{
  "data": [
    { "id": "f7e9…", "verification_id": "KBS-VR-7F3A", "status": "in_review", "supplier": { … } }
  ]
}
GEThttps://api.trust.keybs.io/v1/verifications/:idverification:read

Retrieve a single verification — safe public-facing fields only.

Response

{
  "id": "f7e9…",
  "verification_id": "KBS-VR-7F3A",
  "status": "approved",
  "supplier": { "name": "Acme Trading Co.", "country_code": "IN", "trust_score": 78, "risk_band": "medium" },
  "report": { "lifecycle_status": "published", "signed_at": "…", "valid_until": "…", "verify_url": "…" }
}
POSThttps://api.trust.keybs.io/v1/verifications/:id/documentsdocument:create

Attach a document reference (signed-URL upload performed separately).

Request

{
  "kind": "trade_license",
  "filename": "license.pdf",
  "storage_path": "tenants/…/license.pdf",
  "sha256": "9af…"
}

Response

{ "id": "doc_…", "kind": "trade_license", "status": "received" }
GEThttps://api.trust.keybs.io/v1/reportsreport:read

List signed/published reports owned by the tenant.

Response

{
  "data": [
    { "verification_id": "KBS-VR-7F3A", "lifecycle_status": "published", "signed_at": "…", "valid_until": "…", "verify_url": "…", "pdf_url": "…" }
  ]
}
GEThttps://api.trust.keybs.io/v1/reports/:verificationIdreport:read

Retrieve a single report — public-safe metadata only.

Response

{
  "verification_id": "KBS-VR-7F3A",
  "supplier_name": "Acme Trading Co.",
  "country": "India",
  "trust_score": 78,
  "risk_band": "medium",
  "report_status": "published",
  "lifecycle_status": "published",
  "signed_at": "…", "valid_until": "…", "verify_url": "…", "pdf_url": "…"
}
POSThttps://api.trust.keybs.io/v1/webhooks/testwebhook:manage

Trigger a signed webhook.test event to every active endpoint.

Request

{ "endpoint_id": "wh_…" }

Response

{ "ok": true, "endpoint_id": "wh_…", "event_id": "evt_…" }

Webhooks

KeyBS delivers signed JSON webhooks for every verification and report lifecycle change. Configure endpoints from the client portal → Webhooks. The signing secret is encrypted at rest with AES-256-GCM and never returned after creation.

Headers

  • X-KeyBS-Event-Id: evt_…
  • X-KeyBS-Timestamp: 1717150800 (unix seconds)
  • X-KeyBS-Signature: t=<ts>,v1=<hex_hmac_sha256(secret, `$${ts}.${body}`)>

Verifying a signature

// Node example
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(secret, header, rawBody) {
  const [tPart, sigPart] = header.split(",");
  const ts = tPart.split("=")[1];
  const sig = sigPart.split("=")[1];
  if (Math.abs(Date.now()/1000 - Number(ts)) > 300) return false; // replay
  const mac = createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  return timingSafeEqual(Buffer.from(mac), Buffer.from(sig));
}

Event payload

{
  "event_id": "evt_…",
  "event_type": "report.signed",
  "created_at": "2026-05-31T10:21:00Z",
  "tenant_id": "tnt_…",
  "verification_case_id": "f7e9…",
  "verification_id": "KBS-VR-7F3A",
  "status": "signed",
  "supplier": { "name": "Acme Trading Co.", "country_code": "IN", "trust_score": 78, "risk_band": "medium" },
  "report": { "lifecycle_status": "signed", "signed_at": "…", "valid_until": "…", "verify_url": "…" }
}

Event types

  • verification.submittedNew verification case created (API or portal).
  • verification.in_reviewCase picked up by an analyst.
  • verification.rfi_requestedAnalyst requested more information from the client.
  • verification.rfi_resolvedRFI items have been provided and the case is back in review.
  • verification.approvedCase approved (with or without controls). Report not yet signed.
  • verification.declinedCase declined. No report will be issued.
  • report.signedSenior Analyst signed the report. Cryptographic digest available.
  • report.publishedSigned report published — verify_url is live.
  • report.revokedReport revoked. Public verify page shows a revoked warning.
  • report.expiringReport is within 30 days of valid_until.
  • report.expiredReport has passed valid_until — verify page marked expired.
  • webhook.testSent only by manual test from /app/webhooks.

Retry & delivery log

Failed deliveries (non-2xx or timeout) are marked failed with next_retry_at populated; we do not retry forever. The delivery log includes attempt count, HTTP status, response body excerpt, and error message — visible in /app/webhooks. webhook.test events are tracked separately from lifecycle events.

Badge embed

Paste the snippet on any partner site. The script injects a hosted iframe pointing at the badge page — partners never see private data.

<script async src="https://app.trust.keybs.io/badge/<verificationId>/embed.js"></script>
<div id="keybs-badge"></div>

Hosted badge

https://app.trust.keybs.io/badge/<verificationId>

Badge states

  • Active — within validity window.
  • Expiring — within 30 days of valid_until; warning band shown.
  • Expired — past valid_until; verify page remains accessible but marked expired.
  • Revoked — signing authority revoked the report; do not rely on it.
  • Disputed — under client/regulator dispute; treat as not-current.

Error codes

CodeHTTPMeaning
unauthorized401Missing or invalid Authorization header / API key.
forbidden403Key is valid but the tenant does not own this resource.
invalid_scope403Key lacks the scope required by this endpoint.
not_found404Resource not found within this tenant.
validation_error422Request body failed schema validation. See `message`.
rate_limited429Per-key rate limit exceeded. Retry shortly.
server_error500Unexpected internal error. Includes request_id for support.
{ "error": { "code": "invalid_scope", "message": "Scope verification:create required", "request_id": "req_…" } }

Rate limits

Each API key gets 120 requests / minute by default, enforced per key (and effectively per tenant). Exceeded calls return 429 rate_limited. Enterprise plans lift this — talk to sales.

CORS allowlist

  • https://trust.keybs.io
  • https://app.trust.keybs.io
  • https://ops.trust.keybs.io
  • https://developers.trust.keybs.io

Server-to-server calls (no Origin header) are not affected by CORS.

Sandbox / demo mode

Sandbox keys (kbs_test_…) hit the same endpoints and return the same shapes as production, but target the demo-data tenant. Use them to wire up integrations end-to-end without touching real supplier cases. Sandbox-issued reports carry a demo watermark on the public verify page.