---
title: Authentication
description: Authenticate people, internal services, and future registered agents with WorkOS access tokens while preserving tenant and actor identity.
lastModified: 2026-09-10
search:
  tags: [authentication, workos, permissions]
---

> **Bearer access token**
>
> Billtyme accepts a WorkOS JWT access token in the standard authorization
>   header.
>
> ```http
> Authorization: Bearer YOUR_ACCESS_TOKEN
> ```
>
> Never put credentials in the query string or request body.

## Verification flow

1. **Read the bearer token**

    Reject a missing or malformed authorization header.

2. **Verify the JWT**

    Validate the WorkOS signature, issuer, required claims, expiry, and intended
    recipient.

3. **Derive identity and tenant**

    Build the actor context and derive the tenant exclusively from the verified
    `org_id` claim.

4. **Check the operation permission**

    Authorize the command before invoking the application service.

## Token profiles

**Human**

Use an AuthKit session access token for a direct human or administrative
action. Billtyme retains user and impersonation context for audit history.

**Internal service**

Use a WorkOS Connect machine-to-machine token for the planned SkillSail
Operations integration while it acts as a service.

**Registered agent**

Agent Registration is a future integration for an agent with its own
identity and optional delegated-user context. It can replace M2M for that
agent, but does not replace every service integration.

The verifier supports these identity shapes, but the current product flow has not adopted Agent Registration yet.

## Organization isolation

For [MCP connections](/docs/mcp), the organization selected at sign-in can contain several sending
companies. Tools use the organization-wide default unless an optional `sending_company_id` is
supplied. Company setup, editing, and default selection are available through MCP. Every selected
sender must belong to the authenticated organization; accessing a different organization requires
reconnecting and authorizing it. The company-path behavior below applies to the retained REST API.

> **Tenant identity is not caller input**
>
> The verified `org_id` claim determines the Billtyme tenant. Never send an
> organization ID in the request body, query string, or path; Billtyme does not
> trust those values for tenant selection.

The verified WorkOS organization maps to an internal Billtyme account. Production uses a dedicated database and the WorkOS production environment. Development and Preview may share a non-production database when both use the WorkOS staging environment.

A company is a legal sender inside that account, so company-scoped operations include a `company_id` path parameter. Billtyme verifies that the company belongs to the authenticated account before reading or writing data.

All organization members currently share access to all companies in the account. Company-specific authorization can be added later without changing the API resource model.

Audit events distinguish the caller type and retain delegated or impersonation context when WorkOS supplies it.

## Permissions

Tokens need the permission required by each operation:

- `companies:read`
- `companies:write`
- `customers:read`
- `customers:write`
- `invoices:read`
- `invoices:write`

Permissions are exact; the API does not grant access through a wildcard permission.

## Failure responses

**401 Unauthorized**

The credential is missing, malformed, invalid, expired, or intended for
another recipient.

**403 Forbidden**

The credential is valid, but it lacks the permission required by the
operation.

> **Do not send the WorkOS server key**
>
> Billtyme's server-side `WORKOS_API_KEY` is only for Billtyme calling WorkOS.
> It is never a caller credential and must not be sent to this API.
