Integrations
Integrations let you connect a third-party service once at the organization level, then push its credentials into your projects as environment variables. Connecting Stripe, for example, encrypts your secret key at rest and — when you link it to a project — writes STRIPE_SECRET_KEY (and any metadata-derived vars) into that project’s environment and best-effort redeploys it.
Two distinct systems share the underlying integrations table, keyed by a string type:
- The GitHub App integration (
type: "github", reserved) — used for private-repo project creation. It stores only aninstallation_id; the installation access token is never persisted. - Marketplace integrations — third-party services like Sentry, Stripe, and Resend. Their API key is encrypted at rest (AES-256-CBC) and never returned by the API; reads surface only a masked hint plus display-safe metadata.
There is exactly one connection row per (organization, type). Re-connecting a type overwrites the stored credential and metadata.
The catalog
Section titled “The catalog”The catalog is a static, versioned constant served by the API and rendered in the dashboard — there is no per-org catalog database. Brand logos are pulled from the Simple Icons CDN.
Fetch it with:
GET /v1/integrations/catalog
It returns the full list of catalog entries plus the available categories and kinds used for filtering.
Categories
Section titled “Categories”Entries are grouped into 16 categories:
| All | Monitoring | Logging | Database |
| Storage | Authentication | Analytics | AI |
| Messaging | Payments | Search | Feature Flags |
| Secrets | Backend | Media | CMS |
| Kind | Meaning |
|---|---|
| Native | A first-party / deeply-integrated provider |
| External | A third-party service connected by API key |
Catalog entry shape
Section titled “Catalog entry shape”| Field | Type | Notes |
|---|---|---|
id | string | Stable slug used as the :type in URLs |
name | string | Display name |
category | string | One of the 16 categories above |
kind | Native | External | |
status | live | coming_soon | Only live entries can be connected |
connectionType | api_key | How the credential is supplied |
provisions | boolean? | Deep-provision flag (provisions a real resource) |
description | string | Short blurb |
color | string | Brand color |
iconUrl | string | Simple Icons CDN URL |
website | string | Provider homepage |
envVars | string[] | The env var names this integration injects |
inputFields | object[]? | Extra fields the connect form collects (see below) |
Each inputField has a key (e.g. api_key or metadata.<name>), label, placeholder, type (text or password), a required flag, and an optional hint.
Live integrations
Section titled “Live integrations”These integrations are connectable today. Everything else in the catalog is marked coming_soon.
| Category | Live providers |
|---|---|
| Monitoring | Sentry, Datadog, New Relic, Rollbar |
| Logging | Logtail |
| Messaging | Resend, Slack |
| Payments | Stripe |
| Secrets | Doppler |
| Analytics | PostHog |
| Feature Flags | LaunchDarkly, Statsig |
| AI / Search | Firecrawl |
Coming soon
Section titled “Coming soon”The coming_soon set includes:
- Deep-provision types (
provisions: true) that light up when their provisioners ship:pandastack_postgres,pandastack_ai,upstash,turso,together,replicate,elevenlabs,perplexity. - Providers needing live validators:
aws-s3,auth0. - Others:
axiom,svix,liveblocks,knock,novu,plausible,growthbook,meilisearch,fal,supabase,neon,planetscale,mongodb,pinecone,cloudinary,clerk,convex,inngest,hasura,mux,contentful,sanity,datocms.
Connecting an integration
Section titled “Connecting an integration”Connecting a marketplace integration validates the credential before anything is stored, so an invalid key is never persisted.
POST /v1/integrations/:type/connect — requires member+ role.
The connect use case runs these steps in order:
- Assert connectable — rejects the reserved
githubtype, unknown types, and anycoming_soonentry. - Validate the credential first — the credential is checked against the provider via the injected validator. If validation fails, a user-facing message is thrown and nothing is stored.
- Encrypt the
api_key(AES-256-CBC). - Upsert the single
(org, type)row with the encrypted key plus any metadata.
Re-connecting overwrites both the credential and the metadata.
# Connect Stripecurl -X POST https://api.pandastack.io/v1/integrations/stripe/connect \ -H "Authorization: Bearer $PANDASTACK_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "apiKey": "sk_live_...", "metadata": { "publishable_key": "pk_live_...", "webhook_secret": "whsec_..." } }'Connection status and installed integrations
Section titled “Connection status and installed integrations”Any org member can read connection status. Keys are always masked; secret metadata is stripped from reads.
GET /v1/integrations/:type/status — one integration’s status.
Returns either:
{ "success": true, "data": { "type": "resend", "connected": true, "maskedKey": "re_ab…cd12", "metadata": { "domain": "acme.com" }, "connectedAt": "2026-07-01T12:00:00.000Z" }}or, when not connected:
{ "success": true, "data": { "connected": false } }GET /v1/integrations/statuses — a map of type → status for every connected integration in the org. The reserved github row is excluded.
Masking and secret metadata
Section titled “Masking and secret metadata”The maskedKey never contains the raw key. Certain metadata keys are treated as secrets and are never echoed in a status read:
| Secret metadata key (masked/stripped) |
|---|
webhook_secret |
client_secret |
secret_access_key |
personal_api_key |
All other metadata is display-safe and returned as-is: site, org, host, region, bucket, app_name, domain, project.
Linking to projects (env injection)
Section titled “Linking to projects (env injection)”Env injection is how a connected service actually reaches your running app. Linking a connected integration to one or more projects computes its env vars from the decrypted credential, injects them into each project’s environment, and best-effort redeploys.
POST /v1/integrations/:type/link-projects — requires member+ role.
The link use case:
- Computes env vars from the connection’s decrypted credentials using a static per-integration map.
- Injects them into each owned project’s env (replace-then-append, so it is idempotent).
- Records a
ProjectIntegrationlink that snapshots the injected variable names. - Best-effort redeploys each project — a redeploy failure never fails the link.
It returns which projects were linked and which redeployed.
# Link the Sentry integration to two projectscurl -X POST https://api.pandastack.io/v1/integrations/sentry/link-projects \ -H "Authorization: Bearer $PANDASTACK_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "projectIds": [123, 456] }'What env vars each integration injects
Section titled “What env vars each integration injects”The mapping from a connected integration to project env vars is static. Some examples:
| Integration | Injected env vars |
|---|---|
stripe | STRIPE_SECRET_KEY (+ STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET from metadata) |
sentry | SENTRY_DSN, SENTRY_ORG, SENTRY_PROJECT, SENTRY_AUTH_TOKEN |
resend | RESEND_API_KEY |
posthog | POSTHOG_API_KEY (+ POSTHOG_HOST) |
datadog | DD_API_KEY (+ DD_SITE) |
slack | SLACK_WEBHOOK_URL |
How a database integration injects DATABASE_URL
Section titled “How a database integration injects DATABASE_URL”A deep-provision database integration does not carry a static DATABASE_URL in its metadata map. Instead the provisioner creates a real database resource per project and returns the connection string as DATABASE_URL at provision time. This is why the static env map returns an empty list for those types: the value is only known once the resource exists.
If you provision a PandaStack database directly, its connection URL is available on the database itself — see Connecting to a database.
Unlinking
Section titled “Unlinking”DELETE /v1/integrations/:type/unlink/:projectId — requires member+ role.
Unlinking strips exactly the injected variable names recorded on the link (your own vars are left untouched), deletes the link row, and best-effort redeploys.
List a project’s links with:
GET /v1/integrations/:type/projects
Disconnecting
Section titled “Disconnecting”DELETE /v1/integrations/:type/disconnect — requires member+ role.
Disconnecting cleans up first, then deletes:
- Strips the injected env vars from every linked project (link cleanup runs first).
- Deletes the connection row — the foreign-key cascade drops the link rows.
Cross-tenant or not-connected requests return 404 (tenant-safe).
GitHub App integration
Section titled “GitHub App integration”The GitHub App integration is separate from the marketplace and powers private-repo project creation. It stores only the installation_id.
| Endpoint | Method | Role | Purpose |
|---|---|---|---|
| /v1/integrations/github/connection | GET | member | GitHub App connection status |
| /v1/integrations/github/authorize | POST | member | Save installation_id after App install |
| /v1/integrations/github/repositories | GET | member | List repos via the installation token |
| /v1/integrations/github/branches?repo= | GET | member | List branches for a repo |
| /v1/integrations/github/:installationId | DELETE | member | Remove the GitHub App installation |
Full endpoint reference
Section titled “Full endpoint reference”| Endpoint | Method | Role | Purpose |
|---|---|---|---|
| /v1/integrations/catalog | GET | member | Full marketplace catalog + categories/kinds |
| /v1/integrations/statuses | GET | member | Map type → status for all connected integrations |
| /v1/integrations/:type/status | GET | member | One integration’s status (masked) |
| /v1/integrations/:type/connect | POST | member+ | Connect / reconnect (validate + encrypt + upsert) |
| /v1/integrations/:type/disconnect | DELETE | member+ | Disconnect (strip injected env, delete row) |
| /v1/integrations/:type/projects | GET | member | Projects linked to this integration |
| /v1/integrations/:type/link-projects | POST | member+ | Link to projects (inject env + redeploy) |
| /v1/integrations/:type/unlink/:projectId | DELETE | member+ | Unlink one project (strip env + redeploy) |
All routes use the standard API envelope and require a Bearer token.
Related
Section titled “Related”- Environment variables & secrets — how injected vars appear on a project.
- Integrations API reference
- Databases overview — for the deep-provision database path.
- Plans & limits — analytics and custom domains are paid feature flags.