Third-Party Integrations API

This document describes the third-party integrations endpoints. The platform supports bidirectional sync with Jira, Salesforce, and GitHub, enabling experiment lifecycle events to flow into your existing tooling.


Overview

Each integration is represented as a persisted configuration record containing the service type, credentials, and connection metadata. Incoming events from external services are handled via per-integration webhook endpoints.

Base path: /api/v1/integrations

Authentication: All endpoints require a valid Bearer token. Minimum role: DEVELOPER for create/update/delete; ANALYST for read.


IntegrationType Enum

ValueDescription
JIRAAtlassian Jira — link experiments to issues and sync status
SALESFORCESalesforce CRM — push experiment results to campaign objects
GITHUBGitHub — create issues/PRs and receive webhook events on repo activity

CRUD Endpoints

Create Integration

POST /api/v1/integrations

Creates a new integration configuration. The config object structure varies by integration_type (see Per-Service Config Schemas below).

Authentication: DEVELOPER role required.

Request Body

{
  "name": "My Jira Integration",
  "integration_type": "JIRA",
  "description": "Links experiments to Jira tickets in the EXP project",
  "config": {
    "base_url": "https://your-org.atlassian.net",
    "username": "automation@your-org.com",
    "api_token": "ATATT3x..."
  }
}

Response: 201 Created

{
  "id": "int-uuid-here",
  "name": "My Jira Integration",
  "integration_type": "JIRA",
  "description": "Links experiments to Jira tickets in the EXP project",
  "is_active": true,
  "created_at": "2026-01-15T09:00:00Z",
  "updated_at": "2026-01-15T09:00:00Z",
  "created_by": "usr-uuid-here"
}

List Integrations

GET /api/v1/integrations

Returns all integrations visible to the authenticated user. Optionally filter by type.

Authentication: ANALYST role required.

Query Parameters

ParameterTypeRequiredDescription
typestringNoFilter by IntegrationType value (JIRA, SALESFORCE, GITHUB)
is_activebooleanNoFilter by active/inactive status
skipintNoPagination offset (default: 0)
limitintNoPage size (default: 50, max: 200)

Example Request

curl -X GET "https://your-platform.example.com/api/v1/integrations?type=GITHUB" \
  -H "Authorization: Bearer your_access_token"

Response: 200 OK

[
  {
    "id": "int-uuid-here",
    "name": "GitHub Experiments Repo",
    "integration_type": "GITHUB",
    "description": "Webhook integration for the experiments monorepo",
    "is_active": true,
    "created_at": "2026-01-10T14:22:00Z",
    "updated_at": "2026-01-10T14:22:00Z",
    "created_by": "usr-uuid-here"
  }
]

Get Integration

GET /api/v1/integrations/{id}

Returns the full configuration for a single integration, including credentials (secrets are masked).

Path Parameters

ParameterTypeDescription
idstring (UUID)Integration ID

Response: 200 OK

{
  "id": "int-uuid-here",
  "name": "My Jira Integration",
  "integration_type": "JIRA",
  "description": "Links experiments to Jira tickets",
  "is_active": true,
  "config": {
    "base_url": "https://your-org.atlassian.net",
    "username": "automation@your-org.com",
    "api_token": "***REDACTED***"
  },
  "created_at": "2026-01-15T09:00:00Z",
  "updated_at": "2026-01-15T09:00:00Z",
  "created_by": "usr-uuid-here"
}

Update Integration

PUT /api/v1/integrations/{id}

Updates the configuration of an existing integration. Partial updates are supported — only supply fields you want to change.

Authentication: DEVELOPER role required (or owner).

Request Body (all fields optional)

{
  "name": "Updated Jira Integration",
  "description": "Now points to the NEWPROJ project",
  "config": {
    "base_url": "https://your-org.atlassian.net",
    "username": "new-automation@your-org.com",
    "api_token": "ATATT3x-new-token..."
  }
}

Response: 200 OK — returns the updated integration object.


Delete (Deactivate) Integration

DELETE /api/v1/integrations/{id}

Marks the integration as inactive. Existing webhook subscriptions are revoked. The record is retained for audit purposes.

Authentication: DEVELOPER role required (or owner).

Response: 204 No Content


Webhook Endpoints

Webhook endpoints receive push events from external services. Each webhook validates the incoming payload before processing.

Jira Webhook

POST /api/v1/integrations/{id}/webhook/jira

Receives Jira issue events (created, updated, transitioned). The platform maps Jira issue transitions to experiment lifecycle actions.

Headers required by Jira: Content-Type: application/json

Example Payload (Jira issue transitioned to "Done"):

{
  "webhookEvent": "jira:issue_updated",
  "issue": {
    "id": "10042",
    "key": "EXP-123",
    "fields": {
      "summary": "Checkout Button Color Experiment",
      "status": { "name": "Done" }
    }
  },
  "changelog": {
    "items": [
      { "field": "status", "fromString": "In Progress", "toString": "Done" }
    ]
  }
}

Response: 200 OK

{ "processed": true }

Salesforce Webhook

POST /api/v1/integrations/{id}/webhook/salesforce

Receives Salesforce outbound messages (Campaign updated, Opportunity stage changed). The platform can push experiment results back to associated Salesforce objects.

Headers required: Content-Type: application/json

Example Payload:

{
  "event_type": "campaign_updated",
  "campaign_id": "701xx000000001AAAQ",
  "campaign_name": "Q1 Checkout Optimization",
  "status": "Completed",
  "experiment_key": "checkout-button-color"
}

Response: 200 OK

{ "processed": true }

GitHub Webhook

POST /api/v1/integrations/{id}/webhook/github

Receives GitHub events (push, pull_request, issues). Validates the X-Hub-Signature-256 header using HMAC-SHA256 before processing.

Headers required by GitHub:

HeaderDescription
X-GitHub-EventEvent type (e.g., push, pull_request)
X-Hub-Signature-256HMAC-SHA256 signature of the raw request body, prefixed with sha256=
X-GitHub-DeliveryUnique delivery GUID
Content-Typeapplication/json

Signature Validation

The platform validates the incoming signature using the webhook_secret stored in the integration's config:

expected = "sha256=" + HMAC-SHA256(webhook_secret, raw_body)

Requests with a missing or invalid X-Hub-Signature-256 receive 401 Unauthorized.

Example Payload (pull request opened):

{
  "action": "opened",
  "number": 42,
  "pull_request": {
    "title": "feat: Add new checkout flow experiment",
    "html_url": "https://github.com/your-org/your-repo/pull/42",
    "head": { "ref": "feat/checkout-experiment" },
    "base": { "ref": "main" }
  },
  "repository": {
    "full_name": "your-org/your-repo"
  }
}

Response: 200 OK

{ "processed": true }

Per-Service Config Schemas

Jira Config

Authentication method: HTTP Basic Authusername:api_token encoded as Base64.

FieldTypeRequiredDescription
base_urlstringYesJira instance base URL (e.g., https://your-org.atlassian.net)
usernamestringYesAtlassian account email used for API access
api_tokenstringYesJira API token (generated at id.atlassian.com/manage-profile/security/api-tokens)
{
  "base_url": "https://your-org.atlassian.net",
  "username": "automation@your-org.com",
  "api_token": "ATATT3xFfGF0..."
}

Salesforce Config

Authentication method: OAuth 2.0 Client Credentials — exchanges client_id + client_secret for an access token at the Salesforce token endpoint.

FieldTypeRequiredDescription
instance_urlstringYesSalesforce instance URL (e.g., https://your-org.my.salesforce.com)
client_idstringYesConnected App consumer key
client_secretstringYesConnected App consumer secret
access_tokenstringNoCached OAuth2 access token (managed automatically; supply to pre-seed)
{
  "instance_url": "https://your-org.my.salesforce.com",
  "client_id": "3MVG9...",
  "client_secret": "1234567890ABCDEF..."
}

GitHub Config

Authentication method: Bearer Token — uses a GitHub Personal Access Token (PAT) or GitHub App installation token in the Authorization: Bearer <token> header.

FieldTypeRequiredDescription
tokenstringYesGitHub PAT or GitHub App installation token with appropriate repo scopes
webhook_secretstringYesSecret used to verify incoming X-Hub-Signature-256 webhook signatures
{
  "token": "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "webhook_secret": "a-random-strong-secret"
}

Error Responses

StatusMeaning
400 Bad RequestInvalid request body or missing required config fields
401 UnauthorizedMissing or invalid Bearer token; also returned for GitHub webhooks with invalid signature
403 ForbiddenAuthenticated user lacks required role
404 Not FoundIntegration ID does not exist
409 ConflictAn active integration with the same name and type already exists
422 Unprocessable EntityValidation error on config schema
500 Internal Server ErrorUnexpected server error
{
  "detail": "Integration with name 'My Jira Integration' and type 'JIRA' already exists"
}