Amba

Integrations

Configure third-party integrations — RevenueCat, Superwall, APNs, FCM — with credentials validation.

Integrations live on the control-plane project_integrations table. Configuration is JSONB; the shape depends on the provider (revenuecat, superwall, apns, fcm). APNs and FCM credentials are additionally fetched from GCP Secret Manager when POST /:provider/test is called.

Source: apps/api/src/routes/admin/integrations.ts.

Endpoints

MethodPathDescription
POST/admin/projects/:projectId/integrationsCreate / upsert an integration.
GET/admin/projects/:projectId/integrationsList integrations (metadata only — config redacted).
PATCH/admin/projects/:projectId/integrations/:providerPartial update.
POST/admin/projects/:projectId/integrations/:provider/testValidate credentials.

POST /admin/projects/:projectId/integrations

Upsert by (project_id, provider) — existing integration is replaced.

Request

FieldTypeRequiredDescription
provider"revenuecat" | "superwall" | "apns" | "fcm"yes
configobjectyesProvider-specific config (e.g. webhook_secret, team_id, key_id, service_account_json).

Response 201

{
  "data": {
    "id": "…",
    "project_id": "…",
    "provider": "revenuecat",
    "config": {},
    "is_active": true,
    "webhook_url": "https://api.amba.dev/webhooks/revenuecat?project_id=…"
  }
}

webhook_url is only included for providers that expose an inbound webhook (revenuecat, superwall).

Try it:

POST/admin/projects/%7B%7BprojectId%7D%7D/integrations
developer auth
curl -X POST 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/integrations'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST '${BASE_URL}/admin/projects/{projectId}/integrations' \
  -H 'Authorization: Bearer ${DEV_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d '{}'

GET /admin/projects/:projectId/integrations

Returns integration metadata (no config — credentials stay server-side).

Response 200

{
  "data": [
    { "id": "…", "provider": "revenuecat", "is_active": true, "created_at": "…", "updated_at": "…" }
  ]
}

Try it:

GET/admin/projects/%7B%7BprojectId%7D%7D/integrations
developer auth
curl -X GET 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/integrations'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X GET '${BASE_URL}/admin/projects/{projectId}/integrations' \
  -H 'Authorization: Bearer ${DEV_TOKEN}'

PATCH /admin/projects/:projectId/integrations/:provider

Allowed fields: config, is_active.

Errors

  • 404 NOT_FOUND.

Try it:

PATCH/admin/projects/%7B%7BprojectId%7D%7D/integrations/%7B%7Bprovider%7D%7D
developer auth
curl -X PATCH 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/integrations/%7B%7Bprovider%7D%7D'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X PATCH '${BASE_URL}/admin/projects/{projectId}/integrations/{provider}' \
  -H 'Authorization: Bearer ${DEV_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d '{}'

POST /admin/projects/:projectId/integrations/:provider/test

Validate credentials against the provider. All four providers now make a real outbound call — there is no stub path.

  • apns / fcm — loads credentials (Secret Manager + DB), then calls the provider with a deliberately-invalid token. A 4xx "bad token" response proves the creds are valid; a 401/403 indicates bad creds. Response shape comes from PushSendResult (valid, errorCode, errorMessage).
  • revenuecat — performs an authenticated GET https://api.revenuecat.com/v1/subscribers/<probe> against the secret REST key stored in config.secret_api_key (falls back to config.api_key for older integrations).
  • superwall — performs an authenticated GET https://api.superwall.com/v1/events?limit=1 against config.api_key.

For RevenueCat / Superwall, the response status is classified into one of three buckets:

classificationMeaningTrigger
okCredentials accepted.HTTP 200 or 404 from the provider.
creds_invalidCredentials rejected, or the configured key is blank.HTTP 401 / 403, or a missing/empty key.
provider_unreachableCouldn't get a definitive answer.Network error, DNS failure, 10s timeout, 5xx, or any other unexpected status.

The outbound call is wrapped in a 10-second AbortSignal.timeout so admin requests never wedge on a slow provider. Each test run is logged at info level with provider, projectId, classification, and (when available) providerStatus.

Response 200 — push providers

{ "data": { "provider": "apns", "valid": true, "errorCode": null, "errorMessage": null } }

Response 200 — revenuecat / superwall

{
  "data": {
    "provider": "revenuecat",
    "valid": true,
    "classification": "ok",
    "providerStatus": 200
  }
}
{
  "data": {
    "provider": "superwall",
    "valid": false,
    "classification": "creds_invalid",
    "message": "Provider rejected credentials (HTTP 401)",
    "providerStatus": 401
  }
}
{
  "data": {
    "provider": "revenuecat",
    "valid": false,
    "classification": "provider_unreachable",
    "message": "Provider request failed: timeout"
  }
}

Errors

  • 400 UNSUPPORTED_PROVIDER — provider is outside the schema-allowed set.
  • 404 NOT_FOUND — integration not configured or inactive.
  • 502 CREDENTIAL_LOAD_FAILED — Secret Manager or credential parsing failed (push providers only). error.details includes provider and reason.
  • 500 TEST_FAILED.

Try it:

POST/admin/projects/%7B%7BprojectId%7D%7D/integrations/%7B%7Bprovider%7D%7D/test
developer auth
curl -X POST 'https://api.amba.dev/admin/projects/%7B%7BprojectId%7D%7D/integrations/%7B%7Bprovider%7D%7D/test'
Loading auth… Configure auth in the settings drawer (top-right) to run this request.

Curl:

curl -X POST '${BASE_URL}/admin/projects/{projectId}/integrations/{provider}/test' \
  -H 'Authorization: Bearer ${DEV_TOKEN}' \
  -H 'Content-Type: application/json' \
  -d '{}'