Skip to content

Security & compliance

Introduction

What each API key reaches, which responses carry customer data, and where every secret belongs.

An OnCore integration handles two kinds of sensitive material: the credentials that reach your account, and the conversation records the API hands back (transcripts, recordings, and everything a caller told your assistant). Handling both well is mostly a matter of knowing which key reaches what, and which secrets are allowed to leave your server.

The two key types#

The platform hierarchy is organization → tenants → assistants, and there are two kinds of API key to match it. Both travel in the Authorization header, and both are server-side secrets.

KeyReachesIf it leaks
Tenant keyOne tenant, and every assistant, call, conversation, document and phone number in itThat one tenant
Organization keyAny tenant in the organization (each request names its target with the X-Tenant-Id header), plus the two organization-level audit endpointsEvery tenant in the organization

Use a tenant key for an integration that only ever touches one tenant. Reach for an organization key when one backend serves many tenants and per-tenant credentials would be the harder thing to keep safe, but know that you are trading a narrower blast radius for fewer credentials. Authentication has the exact header format and the failure codes, API keys covers viewing and rotating a key, and Organization-level access covers the multi-tenant rules in full.

Rotation has no overlap window: issuing a new key invalidates the previous one immediately. Treat a rotation as a deploy, not as a background chore.

Sensitive data the API returns#

Almost everything OnCore hands back is a record of a real conversation with a real person, returned in full. It is worth knowing exactly which responses carry it:

  • Call records. A single call read from the calls API carries transcript, summary, analysis, the turn-by-turn messages log, customerNumber (the caller's phone number) and recordingUrl when recording is enabled on the assistant that handled the call.
  • Conversation records. SMS, WhatsApp and web-chat threads read from the conversations API carry the same transcript, summary, analysis and message log, plus customerIdentifier: the phone number or web-chat visitor id the thread belongs to.
  • Bulk exports. The call and conversation export endpoints deliberately return lean rows, without transcripts or recording links. If you are moving history in bulk and do not need the conversation text, the export is the smaller exposure; prefer it.
  • Captured form answers. A capture form delivers its answers to the email addresses and phone numbers named on the form, which makes the form definition also a list of people who receive customer details. Anything the caller said out loud to supply those answers also appears in that call's transcript.
  • Uploaded documents. Knowledge documents are readable back through the API, stored text included. Whatever you upload for an assistant to answer from, any holder of the key can read.
  • Webhook deliveries. The end-of-call and end-of-conversation reports push the same transcript, summary and analysis to your server as each conversation finishes. Your webhook receiver is therefore as sensitive as your key store, and its request logs are a second copy of every conversation.

Two habits are worth building in from the first integration. Log deliberately: a debug dump of one call fetch is a complete transcript sitting in your logging system, usually with wider access than your database. And treat audio as a separate decision from text: recording is a per-assistant switch, and turning it off still leaves you a transcript, summary and analysis for every call. See Call recording & transcripts.

The recording URL is the credential

recordingUrl is not an authenticated endpoint. Fetching it needs no API key, no header and no session: anyone holding the link can download the audio. It does not expire, so a link shared once stays live indefinitely.

What protects a recording is that its URL cannot be guessed: each one carries a long random component generated per call. That makes the URL itself the secret, and it means the usual places links end up are all disclosure:

  • a ticket, a CRM note, or an email thread;
  • a browser's history, or a Referer header if you redirect a user to it;
  • your own application logs, which is the most common one.

Fetch the audio from your server and serve it through a view your users authenticate against. If you must hand a link to a person, treat that as giving away the recording permanently, because you cannot revoke it.

Where each secret belongs#

An integration handles three secrets, and they belong in three different places.

SecretBelongsNever appears in
Tenant or organization API keyYour server, read from a secret managerBrowser JavaScript, a mobile app, a repository, a URL
Web-chat session token, browser-call connection detailsThe visitor's browser (minted server-side, short-lived by design)Anywhere they need to outlive the session
Client server secretYour webhook receiver, which uses it to check inbound deliveriesAny request you send to OnCore

The API key never reaches a browser. Browser-facing features work by exchanging the key server-side for something short-lived and narrow: web chat mints a session token your page then uses as a bearer token, and browser calling returns per-call connection details valid only for a short join window. The visitor holds the short-lived thing; the key stays on your server. The session-minting endpoint is deliberately not callable cross-origin, for exactly that reason. If it works from your page's JavaScript, something is wrong with the design, not with the endpoint.

The client server secret is inbound-only. It is how you prove a delivery came from OnCore: compared against the x-sadie-core-secret header on call, messaging and tool deliveries, or against an HMAC signature on WhatsApp onboarding events. Reject anything that does not match, and never send the secret outbound in a request of your own. Both models are in Webhook verification.

Some 404s are uninformative on purpose. The embed endpoints answer with a bare { "error": "not_found" } for a phone number that is unknown, deleted, or owned by another tenant (identical in all three cases), so the response cannot be used to work out which numbers exist. That is a deliberate anti-enumeration choice, explained in Errors; do not build a probe on top of it.

Deleting data#

The API deletes the things you create: assistants, documents, capture forms, tools, prompts, task groups, and the phone numbers you own. Calls and conversations are the exception: those endpoints are read-only, and there is no endpoint that deletes a call or a conversation record.

Where deletion does happen, OnCore expects it to be reported. If your organization runs its own data-retention cleanup, each run is filed as a retention audit; clearing out a departing customer's data is filed separately as a churn audit. Both take an organization key, both are idempotent on the runId you supply, and both exist so that deletions leave an auditable trail instead of happening silently.

Limits & FAQ#

  • Both key types are server-side secrets. There is no publishable or read-only key variant, so anything a browser can see is something a visitor can use.
  • Key scope is tenant or organization: there is nothing narrower. No per-assistant key, no read-only key, no role.
  • An organization key is strictly more powerful than any tenant key. One leak exposes every tenant in the organization, which is why every cross-tenant request still has to name its tenant explicitly.
  • Recording is per assistant, not per call. There is no in-call opt-out; a line that must not be recorded needs its own assistant with recording switched off.

Can we give one of our customers read-only access to their own data? Not through a key, because key scope stops at the tenant. Build the narrower view yourself: keep the key on your server and expose only the calls, conversations and fields that customer is entitled to see.

How much should we copy into our own systems? As little as the integration actually needs. Every copy of a transcript is another thing you have to protect and another place a deletion has to reach. If storing an id and fetching on demand will do, prefer that.

Does the API expose an access log of its own requests? No. If you need a record of which of your systems read which call, log it at your own gateway or client wrapper.

Compliance claims are outside this public contract

These developer docs make no claim about certifications, encryption guarantees, data residency, retention windows, sub-processors, breach notification, or how OnCore fulfils an end-customer deletion request. Treat those items as unspecified here and rely only on the contractual compliance terms supplied for your OnCore account before processing regulated data.

Next steps#