SaaS platform integration
Embed Sentisec across your multi-tenant customer base ā one workspace per customer, provisioned from your signup flow, with BYOK or pooled provider credentials.
If you build an agent product ā a chat platform, a low-code automation surface, a vertical AI assistant ā every customer who signs up runs agents against an LLM. Sentisec gives each of them their own integrity engine, dashboard, forensic record, and isolation boundary, without you operating a security stack.
The shape is: one Sentisec workspace per customer. Their credentials, their keys, their sessions, their dashboard view, with no cross-tenant leakage on the integrity surface.
The mapping
| Your product | Sentisec |
|---|---|
| Your customer / tenant | Workspace |
| Your billing customer | Workspace owner (Clerk organization) |
| Their LLM provider key (bring your own key) | Workspace upstream credential |
| Your customer's signup event | Workspace creation + first API key |
| Their API call to your backend | Forwarded to api.sentisec.ch with their workspace's Sentisec key |
| Their agent's tool calls | Evaluated against their workspace's policy |
| Their session timeline | Their workspace dashboard view |
Wire your LLM calls per customer
This is the whole integration. Both halves of the request are per-tenant: that customer's workspace id in the URL, that customer's Sentisec key in the header.
import anthropic
SENTISEC = "https://api.sentisec.ch"
def call_llm_for_customer(customer, prompt):
client = anthropic.Anthropic(
base_url=f"{SENTISEC}/ws/{customer.sentisec_workspace_id}",
api_key=decrypt(customer.sentisec_api_key),
)
return client.messages.create(
model="claude-sonnet-4",
max_tokens=512,
messages=[{"role": "user", "content": prompt}],
)For OpenAI-shaped clients, append /v1 to that same base URL.
The workspace id is the isolation unit. Read it from the customer record ā a hardcoded id routes every customer's traffic through a single workspace and resolves one shared policy instead of each customer's own.
Nothing else about your agent code changes. See the framework recipes if your product uses LangChain, LlamaIndex, CrewAI, or another framework rather than a raw provider client.
Provision a workspace per customer
Three programmatic patterns, plus a manual fallback for early evaluation.
Pattern A ā Clerk webhook (workspace created for you)
If your product uses Clerk for auth, Sentisec can create the workspace the moment a customer signs up. One webhook configuration, no per-customer provisioning code.
In the Sentisec dashboard, configure /webhooks/clerk as a Clerk
webhook destination for your parent organization. Subscribe to
user.created and organization.created.
When a customer signs up, your existing Clerk flow fires the event.
Sentisec verifies the signature and creates the matching workspace ā
personal for user.created, organization-scoped for
organization.created. The workspace is usable for traffic
immediately.
Mint the customer's first API key with one call to
POST /ws/{workspace_id}/api-keys (see Pattern B), or let the customer
do it themselves in the dashboard.
The webhook acknowledges with {"status":"ok"}. It is a provisioning
signal, not a key-delivery channel ā the response body carries no key, and
API keys are only ever returned in plaintext by the mint endpoint below.
The receiver is idempotent: a duplicate delivery (same svix-id) returns
{"status":"duplicate"} and does not create a second workspace, so retries
are safe. An unsigned request is rejected with 400 missing_header, and an
event type Sentisec does not provision on returns {"status":"ignored"}.
Pattern B ā REST API (two calls from your signup handler)
If you do not use Clerk, will not share your webhooks, or simply want the workspace id and key returned inline, call the API from your own customer-signup handler.
POST https://api.sentisec.ch/v1/workspaces
Authorization: Bearer <your service-account Clerk JWT>
Content-Type: application/json
{ "name": "customer-acme-corp" }
ā 201 Created
{
"workspace": {
"id": "<uuid>",
"name": "customer-acme-corp",
"clerk_org_id": null,
"owner_clerk_user_id": "user_...",
"tier": "free",
"created_at": "..."
}
}POST https://api.sentisec.ch/ws/{workspace_id}/api-keys
Authorization: Bearer <your service-account Clerk JWT>
Content-Type: application/json
{ "name": "customer-acme-corp-prod" }
ā 201 Created
{
"api_key": { "id": "...", "name": "...", "last_four": "...", "created_at": "..." },
"plaintext": "sk_sentisec_a1b2c3d4_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}plaintext is returned once. Store it against the customer record,
encrypted at rest ā the same posture you already use for any third-party key
you hold on a customer's behalf. If you lose it, mint another key; you
cannot re-read the original.
Wired into a signup handler:
import requests
SENTISEC = "https://api.sentisec.ch"
def provision_sentisec_for_customer(customer):
"""Called once per customer signup."""
admin_jwt = mint_service_token(role="org:admin")
headers = {"Authorization": f"Bearer {admin_jwt}"}
ws = requests.post(
f"{SENTISEC}/v1/workspaces",
headers=headers,
json={"name": f"cust-{customer.id}"},
timeout=5,
).json()["workspace"]
minted = requests.post(
f"{SENTISEC}/ws/{ws['id']}/api-keys",
headers=headers,
json={"name": f"cust-{customer.id}-prod"},
timeout=5,
).json()
customer.sentisec_workspace_id = ws["id"]
customer.sentisec_api_key = encrypt(minted["plaintext"])
customer.save()Both endpoints authenticate with a Clerk JWT carrying org_role: "org:admin" for your parent organization. Mint those service-account tokens
with Clerk's Backend
API ā typically a
long-lived machine token bound to a service-account user in your parent org.
Pattern C ā Pair code (your end-user authorizes it)
If you want each end-user to authorize Sentisec themselves from inside your product rather than your backend doing it on their behalf, use the pair-code flow. The split of authority is deliberate: minting a code requires your admin credentials, while redeeming one does not ā the short-lived code is itself the proof.
Your backend, holding the admin Clerk JWT, calls
POST /v1/sdk/pair/start. It returns a single-use opaque code valid for
10 minutes. Hand that code to your customer's client.
The user goes to app.sentisec.ch/sdk-pair ā or a paste box you embed
in your own product ā enters the code, and approves the workspace
binding.
Your client polls POST /v1/sdk/pair/complete with the code, with
no Clerk JWT needed, and receives the workspace API key once it is
redeemed. A code that is unknown, expired, or already used returns
410 code_unredeemable.
Pattern D ā Manual dashboard (evaluation only)
For a five-customer pilot, click through app.sentisec.ch: new workspace ā
name it ā mint key ā copy. Do not ship this past evaluation; the
programmatic patterns scale and audit cleanly.
Picking a pattern
| Your situation | Pattern |
|---|---|
| You use Clerk and can route webhooks | A ā Clerk webhook |
| You want the id and key returned inline | B ā REST API |
| Per-end-user opt-in inside your product | C ā Pair code |
| One to five customer evaluation | D ā Manual |
Picking a credential lane
| Your business model | Lane |
|---|---|
| Your customer brings their own LLM key | Each workspace holds that customer's provider credential |
| You include LLM usage in your subscription | You set a platform credential on each workspace and bill in your own model |
| Mix ā free tier pooled, paid tier customer-owned | Both; the workspace credential is what the request uses |
Your customer can paste their own provider key into their workspace from
app.sentisec.ch, or through a settings screen you embed in your product.
Either way the key lives on the workspace, encrypted, and never in your
application config.
Provider coverage
| Provider | Available |
|---|---|
| Anthropic (Claude) | Yes |
| OpenAI | Yes |
| Moonshot Kimi | Yes |
| Google Gemini | Yes |
| Google Vertex AI | Yes |
| Amazon Bedrock | Yes |
| Azure OpenAI | Not yet ā get in touch |
Each is configured as a workspace credential. See Providers for the per-provider setup, and what each transport covers for the capability map. If your customers depend on a provider that is not listed, tell us ā that is what orders the list.
Give your customers visibility
Link a customer to their own workspace view at
https://app.sentisec.ch/w/<workspace-id>/sessions once they have
authenticated through your shared Clerk organization. They see their
sessions, verdicts, and forensic evidence ā scoped to their workspace only.
If you would rather render that inside your own product, the read-only APIs in the API reference let you build a passthrough view.
Single-sign-on embedding ā your auth flow straight into their Sentisec workspace with no second login ā is not available yet. Talk to us if it is a blocker for your rollout.
Tools your platform runs server-side
If your platform's own executor runs the tools the model requests ā the "toolbox" pattern, where tool definitions live in your backend rather than in the LLM context ā the managed proxy sees the model's intent but not your execution. Gating those server-side runs at full fidelity is an in-process integration rather than a base-URL swap. Request access for the in-process pattern and the policy authoring guide.
When you need self-host
Self-host the full stack when:
- Your customers' data cannot leave their region.
- You serve regulated industries that prohibit shared-tenancy security infrastructure.
- Your enterprise contracts require single-tenant control-plane isolation.
Self-host is a contracted deployment; plan a one-to-two week pilot. Request access ā