Providers
A provider is a registered external API that modules call through
the Integration Hub. Each tenant manages their own set of providers —
a provider registered by one tenant is invisible to all others (RLS
enforced by tenant_id).
Provider Model
| Field | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
name | string | Human-readable name (e.g. "Stripe Production", "Twilio SMS") |
type | enum | Provider category — see types below |
baseUrl | string | Base URL of the external API (e.g. https://api.stripe.com) |
authType | enum | Authentication method — see auth types below |
credentials | encrypted JSON | API keys, tokens, secrets. AES-256-GCM encrypted at rest. |
config | JSON | Per-provider config: timeout_ms, retries, custom headers |
tenantId | UUID | Owner tenant |
status | enum | active, degraded, down, disabled |
circuitBreakerState | enum | closed, half_open, open |
createdAt | ISO 8601 | Creation timestamp |
updatedAt | ISO 8601 | Last update timestamp |
Provider Types
| Type | Use cases |
|---|---|
payment | Stripe, PayPal, Adyen, CloudPayments |
sms | Twilio, AWS SNS, MessageBird |
email | SendGrid, Mailgun, Amazon SES |
analytics | Amplitude, Mixpanel, Google Analytics |
advertising | Facebook Ads, Google Ads, TikTok |
custom | Any other external HTTP API |
Auth Types
| Auth type | How credentials are injected | Credentials structure |
|---|---|---|
api_key | X-API-Key: {key} header | { "apiKey": "sk_live_..." } |
bearer | Authorization: Bearer {token} | { "token": "eyJ..." } |
basic | Authorization: Basic {base64(user:pass)} | { "username": "...", "password": "..." } |
oauth2 | Access token obtained via client credentials flow, auto-refreshed | { "clientId": "...", "clientSecret": "...", "tokenUrl": "..." } |
hmac | Request body signed with secret (e.g. Stripe webhook-style outgoing) | { "secret": "whsec_...", "algorithm": "sha256" } |
Auth headers are injected by the Hub at call time. Credentials are never returned to the caller — the Hub reads the encrypted value from PostgreSQL, decrypts it in memory using the Vault-managed AES-256-GCM key, and injects it into the outgoing HTTP request.
Credential Encryption
Register provider (POST /api/v1/integrations/providers):
credentials field (plaintext in request body, TLS protected)
↓ Hub receives
Hub fetches encryption key from HashiCorp Vault
Hub encrypts: AES-256-GCM(credentials JSON, key)
Hub stores encrypted bytes in PostgreSQL credentials column
Hub returns provider record (credentials field OMITTED from response)
Call provider (POST /api/v1/integrations/call):
Hub reads encrypted credentials from PostgreSQL
Hub fetches key from Vault
Hub decrypts in-memory (AES-256-GCM)
Hub injects into outgoing request
Plaintext credentials never leave the Hub process
Credentials are never returned in any API response — not in GET,
not in PATCH, not in health checks. The credentials field is always
omitted or shown as "[encrypted]" in responses.
Provider Status
| Status | Meaning |
|---|---|
active | Provider is healthy, circuit breaker CLOSED |
degraded | Provider is responding but latency is above threshold (response time > 2s) |
down | Circuit breaker is OPEN — provider is not reachable |
disabled | Manually disabled by the tenant admin — no calls made |
Status is computed by the Hub and stored in Valkey. It is updated:
- After every successful/failed call
- By the background health check worker (every 60 seconds)
- Immediately when the circuit breaker changes state
Register a Provider
POST https://api.septemcore.com/v1/integrations/providers
Authorization: Bearer <access_token>
Content-Type: application/json
{
"name": "Stripe Production",
"type": "payment",
"baseUrl": "https://api.stripe.com",
"authType": "bearer",
"credentials": {
"token": "sk_live_abc123def456"
},
"config": {
"timeout_ms": 10000,
"retries": 3
}
}
Response 201 Created:
{
"id": "01j9pint0000000000000000",
"name": "Stripe Production",
"type": "payment",
"baseUrl": "https://api.stripe.com",
"authType": "bearer",
"credentials": "[encrypted]",
"config": { "timeout_ms": 10000, "retries": 3 },
"tenantId": "01j9ten0000000000000000",
"status": "active",
"circuitBreakerState": "closed",
"createdAt": "2026-04-22T01:50:00Z",
"updatedAt": "2026-04-22T01:50:00Z"
}
Get Provider List
GET https://api.septemcore.com/v1/integrations/providers?page=1&limit=20
Authorization: Bearer <access_token>
{
"items": [
{
"id": "01j9pint0000000000000000",
"name": "Stripe Production",
"type": "payment",
"status": "active",
"circuitBreakerState": "closed",
"updatedAt": "2026-04-22T01:50:00Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}
Update Provider
PATCH https://api.septemcore.com/v1/integrations/providers/01j9pint0000000000000000
Authorization: Bearer <access_token>
Content-Type: application/json
{
"credentials": {
"token": "sk_live_newkey789"
},
"config": {
"timeout_ms": 15000
}
}
Response 200 OK — same shape as 201, credentials field always
shown as "[encrypted]".
Provider Health Check
GET https://api.septemcore.com/v1/integrations/providers/01j9pint0000000000000000/health
Authorization: Bearer <access_token>
{
"providerId": "01j9pint0000000000000000",
"name": "Stripe Production",
"status": "active",
"circuitBreakerState": "closed",
"latencyMs": 142,
"lastCheckedAt": "2026-04-22T01:49:00Z",
"lastErrorAt": null,
"lastError": null
}
When the circuit breaker is OPEN:
{
"providerId": "01j9pint0000000000000000",
"name": "Stripe Production",
"status": "down",
"circuitBreakerState": "open",
"latencyMs": null,
"lastCheckedAt": "2026-04-22T01:49:00Z",
"lastErrorAt": "2026-04-22T01:48:55Z",
"lastError": "connection timeout after 10000ms"
}
Delete Provider
DELETE https://api.septemcore.com/v1/integrations/providers/01j9pint0000000000000000
Authorization: Bearer <access_token>
Response 204 No Content.
Deletion is a soft delete — the provider record is marked as deleted, credentials are purged from the database, and any outstanding DLQ entries for this provider are also soft-deleted.
Error Reference
| Scenario | HTTP | Problem type |
|---|---|---|
| Provider not found | 404 | not-found |
| Duplicate name for tenant | 409 | conflict |
| Invalid credentials format | 400 | validation-error |
| Circuit breaker OPEN | 503 | service-unavailable |
| Outgoing rate limit exceeded | 429 | rate-limit-exceeded |
| External API timeout | 504 | gateway-timeout |