BETA
Skip to content

Integrations

Connect external tools to your audit workflow. The Audit API tracks one row per provider per project, with the providers fixed to: ga4, gsc, sentry, github, gitlab, jira, linear, cloudflare.

Base URL: https://app.krafter.dev/api/v1

List Integrations

Retrieve the integration record for every supported provider. The response always contains one entry per provider — providers that have never been connected return a placeholder row with status: "disconnected" and id: null.

GET /orgs/:org_id/projects/:project_id/audit/integrations

Required scope: audit:read

Example Request

bash
curl https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/integrations \
  -H "Authorization: Bearer kr_live_abc123def456"

Example Response

json
{
  "data": [
    {
      "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
      "org_id": "11111111-1111-1111-1111-111111111111",
      "project_id": "production-website",
      "provider": "github",
      "status": "connected",
      "last_sync_at": "2025-06-10T14:00:00Z",
      "created_at": "2025-06-01T10:00:00Z",
      "updated_at": "2025-06-10T14:00:00Z"
    },
    {
      "id": "2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e",
      "org_id": "11111111-1111-1111-1111-111111111111",
      "project_id": "production-website",
      "provider": "jira",
      "status": "connected",
      "last_sync_at": "2025-06-10T13:45:00Z",
      "created_at": "2025-06-02T11:00:00Z",
      "updated_at": "2025-06-10T13:45:00Z"
    },
    {
      "id": null,
      "provider": "sentry",
      "status": "disconnected",
      "credentials": {},
      "last_sync_at": null,
      "inserted_at": null,
      "updated_at": null
    }
  ],
  "meta": {
    "request_id": "U-cmqcgdZ6jluPPPSiy"
  },
  "error": null
}

status is one of connected, disconnected, syncing, error. Connected and previously-connected rows include created_at and updated_at. Placeholder rows for never-connected providers include the literal field inserted_at (instead of created_at) and an empty credentials: {} field. Real credentials are never echoed in either shape — see Security below.

Response is not paginated

The integrations list is a fixed-size array (one entry per supported provider) and is not wrapped with meta.total / meta.next_cursor.


Connect Integration

Connect (or re-connect) a provider for a project. The request body is provider-agnostic: pass the provider's secrets in the credentials map. The body is upserted on the unique (team, project, provider) key, so calling connect again for the same provider replaces the stored credentials.

POST /orgs/:org_id/projects/:project_id/audit/integrations/:provider/connect

Required scope: audit:write

Path Parameters

ParameterTypeDescription
providerstringOne of: ga4, gsc, sentry, github, gitlab, jira, linear, cloudflare. Any other value returns 422 invalid_provider.

Request Body

FieldTypeRequiredDescription
credentialsobjectNoProvider-specific credentials (token, refresh token, etc.). Stored encrypted at rest.
auth_codestringNoShort-lived OAuth authorization code. When present, it is merged into credentials under the key auth_code before encryption.

The credentials map is opaque to the API — the per-provider key names (e.g. token, api_token, repository, base_url) are conventions interpreted by the worker that talks to the provider, not validated by the connect endpoint. The dashboard OAuth/token-paste flow at app.krafter.dev/audit/integrations is the canonical source for which keys each provider expects.

Example Request (GitHub via personal access token)

bash
curl -X POST https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/integrations/github/connect \
  -H "Authorization: Bearer kr_live_abc123def456" \
  -H "Content-Type: application/json" \
  -d '{
    "credentials": {
      "token": "ghp_xxxxxxxxxxxx",
      "repository": "acme/web-app"
    }
  }'

Example Request (Provider via OAuth auth code)

bash
curl -X POST https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/integrations/sentry/connect \
  -H "Authorization: Bearer kr_live_abc123def456" \
  -H "Content-Type: application/json" \
  -d '{
    "auth_code": "abc123def456"
  }'

Example Response

json
{
  "data": {
    "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "org_id": "11111111-1111-1111-1111-111111111111",
    "project_id": "production-website",
    "provider": "github",
    "status": "connected",
    "last_sync_at": "2025-06-10T14:30:00Z",
    "created_at": "2025-06-01T10:00:00Z",
    "updated_at": "2025-06-10T14:30:00Z"
  },
  "meta": {
    "request_id": "V-dnrdheA7kmvQQQTjz"
  },
  "error": null
}

last_sync_at is set to the connect timestamp because the upsert refreshes that column. Credentials are never returned.

Error Response

json
// 422 Unprocessable Entity — unsupported provider, or changeset failure
{
  "data": null,
  "meta": {
    "request_id": "W-eoseifB8lnwRRRUka"
  },
  "error": {
    "code": "invalid_provider"
  }
}

Sync Integration

Trigger a sync with a connected provider. The integration is moved to status: "syncing" synchronously and a background worker pushes/pulls findings and tasks against the external system. Unlike the dashboard form, the API endpoint takes no body — it always syncs everything pending for that provider.

POST /orgs/:org_id/projects/:project_id/audit/integrations/:provider/sync

Required scope: audit:write

Path Parameters

ParameterTypeDescription
providerstringOne of: ga4, gsc, sentry, github, gitlab, jira, linear, cloudflare.

Request Body

This endpoint accepts no fields. Any body sent is ignored.

Example Request

bash
curl -X POST https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/integrations/github/sync \
  -H "Authorization: Bearer kr_live_abc123def456"

Example Response

json
// 202 Accepted
{
  "data": {
    "id": "1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "org_id": "11111111-1111-1111-1111-111111111111",
    "project_id": "production-website",
    "provider": "github",
    "status": "syncing",
    "last_sync_at": "2025-06-10T14:00:00Z",
    "created_at": "2025-06-01T10:00:00Z",
    "updated_at": "2025-06-10T14:30:00Z"
  },
  "meta": {
    "request_id": "X-fptfjgC9moxSSSVlb"
  },
  "error": null
}

The response reflects the row mid-sync. Poll List Integrations to see when status flips back to connected (or to error if the worker failed) and last_sync_at advances.

Error Responses

json
// 422 Unprocessable Entity — unsupported provider
{
  "data": null,
  "meta": {
    "request_id": "X-fptfjgC9moxSSSVlb"
  },
  "error": {
    "code": "invalid_provider"
  }
}
json
// 422 Unprocessable Entity — sync could not be enqueued (transient infra issue)
{
  "data": null,
  "meta": {
    "request_id": "X-fptfjgC9moxSSSVlb"
  },
  "error": {
    "code": "request_failed"
  }
}

Security

Integration credentials submitted to connect (e.g. GitHub token, Jira API token, OAuth auth_code) are encrypted at rest using AES-256-GCM via the platform's Krafter.Secrets vault before being written to the database. The encrypted payload is stored as opaque ciphertext in the audit_integrations.credentials column and is decrypted only when a worker needs to call out to the provider.

Credential material is never echoed back in any API response: connect, sync, and connected entries returned by list omit the credentials field entirely. Disconnected provider entries returned by list include credentials: {} only as a placeholder — never an actual secret.

Built by Krafter Studio