Tenant/User Model
Every connection in Authpipe is scoped to a tenant (your customer’s organization). The attachment model on the provider config determines whether credentials are shared per-org or per-user.
Tenant-attached ("tenant")
Section titled “Tenant-attached ("tenant")”One credential per tenant. The connection belongs to the organization, not a specific user.
Provider Config (attachment: "tenant")└── Tenant "org_acme" └── 1 connection (shared by all users)Use when:
- The credential represents the org’s integration (e.g., a shared Frame.io token)
- Any authorized user can set up the connection for the whole team
- You don’t need per-user credentials
Behavior:
getCredentiallooks up bytenant_idonlyuser_idis accepted but ignored for resolution- If a different user re-authorizes the same tenant, the existing connection is updated (upsert) — no duplicate is created
- The
user_idon the connection reflects who last authorized
Example
Section titled “Example”// Any user in org_acme gets the same credentialconst result = await authpipe.getCredential({ provider: "frame-io", tenantId: "org_acme",});result, err := client.GetCredential(ctx, &authpipe.GetCredentialParams{ Provider: "frame-io", TenantID: "org_acme",})result = client.get_credential( provider="frame-io", tenant_id="org_acme",)User-attached ("user")
Section titled “User-attached ("user")”One credential per user within a tenant. Each user has their own connection.
Provider Config (attachment: "user")└── Tenant "org_acme" ├── Connection for user_jane ├── Connection for user_bob └── Connection for user_aliceUse when:
- Each user needs their own token (e.g., personal Google Calendar)
- Actions should be attributed to specific users
- The provider’s API requires user-level authorization
Behavior:
getCredentiallooks up bytenant_id+user_iduser_idis required when creating auth sessions
Example
Section titled “Example”// Each user has their own credentialconst result = await authpipe.getCredential({ provider: "google-calendar", tenantId: "org_acme", userId: "user_jane",});result, err := client.GetCredential(ctx, &authpipe.GetCredentialParams{ Provider: "google-calendar", TenantID: "org_acme", UserID: "user_jane",})result = client.get_credential( provider="google-calendar", tenant_id="org_acme", user_id="user_jane",)Choosing an attachment model
Section titled “Choosing an attachment model”| Question | Tenant | User |
|---|---|---|
| Is the credential shared across the org? | Yes | No |
| Does each user need their own token? | No | Yes |
| Can any admin set it up for everyone? | Yes | No |
| Example providers | Frame.io, Jira (project-level), Notion (workspace) | Google Calendar, GitHub (personal), Slack (user token) |
Set the attachment when creating the provider config:
await authpipe.createProviderConfig({ provider_id: "google-calendar", attachment: "user", // ...});Uniqueness constraints
Section titled “Uniqueness constraints”Authpipe enforces uniqueness at the database level:
- Tenant-attached: One active connection per
(workspace, provider_config, installation, tenant_id) - User-attached: One active connection per
(workspace, provider_config, installation, tenant_id, user_id)
Re-authorization upserts (updates tokens) rather than creating duplicates.
Resolution with installations
Section titled “Resolution with installations”When a provider has both an installation and user connections, getCredential with credential_for: "any" (the default) tries the connection first, then falls back to the installation. See Installations for details.