---
title: "Create connection"
method: POST
path: "/connections"
tags: ["connections"]
---

# Create connection

`POST /connections`

## Request

- Base URL: `https://api.instruxi.dev/api/v1/enforcer`
- URL: `https://api.instruxi.dev/api/v1/enforcer/connections`
- Auth: one of:
  - API key in header `Authorization`
  - API key in header `X-API-Key`

Creates a connection in the CALLER's tenant (tenant_admin/admin only), or — for a platform admin whose role carries the cross_tenant grant — in the tenant named by the optional `tenant_id`. `tenant_id` omitted or equal to the caller's own tenant behaves exactly as before. A caller WITHOUT cross_tenant naming any other tenant gets 403 (never silently written to their own tenant); an unknown or deleted target tenant is 404; a target that already has a connection for this provider is 409 (update that one instead). config is sealed with AES-256-GCM before it touches the database and is never returned in plaintext by this or any other route — Create's response has the same redaction as Get. provider_id must resolve to a known provider (400 otherwise). A tenant-shaped body key other than `tenant_id` (e.g. `tenantId`) is a 400 unknown_param rather than being ignored. The audit event records both the caller's and the target tenant.

## Request body

- EnforcerV3InternalUsecaseConnectionsCreateInput
  - `config` object
  - `name` string
  - `provider_id` string
  - `tenant_id` string: TenantID optionally names the tenant the connection is created IN. Omitted or equal to the caller's own tenant: unchanged caller-tenant behaviour. Any OTHER tenant requires the caller's role to carry the cross_tenant grant (platform admin) — anyone else is refused with 403, never silently narrowed to their own tenant. Lets a platform admin configure a new workspace's SendGrid/Twilio/storage before anyone can sign in to it.

## Response `201`

Created

- InternalDeliveryHttpHandlerConnectionResponse
  - `data` EnforcerV3InternalDomainConnectionTenantConnection
    - `config` object: Config is the decrypted credential map — populated only on authorized single-row reads, never on lists.
    - `created_at` string
    - `id` string
    - `is_active` boolean
    - `name` string
    - `provider` EnforcerV3InternalDomainConnectionProvider
      - `capability` EnforcerV3InternalDomainConnectionCapability
        - `created_at` string
        - `description` string
        - `id` string
        - `name` string
        - `updated_at` string
      - `capability_id` string
      - `created_at` string
      - `description` string
      - `id` string
      - `name` string
      - `updated_at` string
    - `provider_app_id` string: ProviderAppID is the provider's PUBLIC application id (e.g. the Privy app id). Plaintext (not in the sealed config) so it can be queried for login tenant resolution and served to the client SDK. app_secret stays in config_encrypted.
    - `provider_id` string
    - `send_enforcement` boolean: SendEnforcement marks this connection's tenant as wallet-send-enforced (multi-sig): the tenant's Privy smart wallets get the mandatory app-key owner quorum and rwa's signing gateway enforces send policy. It lives on the connection row — the mode is baggage of the cybrid-bank wiring for specific tenants, not a property of every Privy tenant. Plaintext (not in the sealed config) so operators flip it with one UPDATE. A tenant is enforced iff ANY active connection row carries the flag; when rotating the flagged connection, set it on the replacement row or enforcement drops.
    - `tenant_id` string
    - `updated_at` string
  - `success` boolean

## Other responses

- `400`: Bad Request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not Found
- `409`: Conflict
- `500`: Internal Server Error

---

[API](https://skmtc.dev/instruxi/apis/enforcer-api.md) · [All operations](https://skmtc.dev/instruxi/apis/enforcer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/instruxi/apis/enforcer-api/revisions/fa82bc1becc0?raw)
