# Azure DevOps integration

Two paths into ActivityLog from Azure DevOps:

1. **Service Hooks (webhooks)** — recommended for operational events: code pushes, PRs, work items, builds, releases. Real-time per event.
2. **Event Grid** — alternative for org-level admin events via Service Bus relay or custom topic. Same destination endpoint as webhooks.

Both land in a single ActivityLog system using one `alw_…` URL token.

## Quick path — Service Hooks

### 1. Mint a webhook URL token

In ActivityLog portal → Systems → **New System** named `devops` (or your preferred name) → **New Token** → check **URL token** → pick **1825-day retention** → **Generate** → copy.

Your full URL:
```
https://api.activitylog.com/api/v1/webhooks/alw_REPLACE_WITH_YOUR_TOKEN
```

See [`53-ingest-webhooks.md`](./53-ingest-webhooks.md) for the underlying webhook contract.

### 2. Configure ADO Service Hooks

In your ADO project → **Project Settings → Service hooks → + Create subscription**. For each event type you want, repeat:

| Service | Trigger | Action URL |
|---|---|---|
| Web Hooks | Code pushed | `https://api.activitylog.com/api/v1/webhooks/alw_REPLACE_WITH_YOUR_TOKEN` |
| Web Hooks | Pull request created | same |
| Web Hooks | Pull request updated | same |
| Web Hooks | Work item created | same |
| Web Hooks | Work item updated | same |
| Web Hooks | Build completed | same |
| Web Hooks | Release deployment completed | same |

Settings on each subscription:

- **Basic authentication**: leave blank.
- **Resource details to send**: All.
- **Messages to send**: All.
- **Detailed messages to send**: All.

Click **Test** before saving — you should see HTTP 201 in the response.

### 3. Verify in ActivityLog

Trigger one of the events (push a commit, open a PR) and check the **Messages** page in the portal. The event should appear within a couple of seconds, tagged with `type: "git.push"` / `pullrequest.created` / etc.

## Event types you'll see

Each ADO event becomes one `Message`. The `type` field captures the source event name.

| ADO event | ActivityLog `type` | Notes |
|---|---|---|
| Code pushed | `git.push` | One message per push |
| Pull request created | `pullrequest.created` | Includes PR title, source/target branch in body |
| Pull request updated | `pullrequest.updated` | Status changes, new commits |
| Pull request merged | `pullrequest.merged` | Captured as an `updated` event with status |
| Work item created | `workitem.created` | Includes ID, title, type, area path |
| Work item updated | `workitem.updated` | Includes diff of changed fields |
| Build completed | `build.complete` | Pipeline ref, result, queue/start/finish times |
| Release deployment | `ms.vss-release.deployment-completed-event` | Environment, release name, result |

For the full catalogue with metadata keys per event, see [`../../../ActivityLog-Integrations/DevOps/SPEC.md`](../../../ActivityLog-Integrations/DevOps/SPEC.md).

## Storage mode

DevOps webhooks ship in **Mode 2 (structured event payload)** by default on Pro and Enterprise — the full 3–18 KB ADO-curated JSON payload is stored as-is in `body`. This preserves source-curated context (commit lists, PR descriptions, build pipeline refs) that would be lost if we re-projected.

On the **Free tier**, payloads are auto-projected down to ~1 KB lean metadata (event_type, actor, target, summary). The full payload is discarded — Free can't store 18 KB ADO payloads without blowing the 16 KB body cap. See [`53-ingest-webhooks.md`](./53-ingest-webhooks.md#free-tier-auto-projection).

## Event Grid alternative

If you have an Event Grid topic that already aggregates ADO events (some Fyin-internal setups do this for cross-subscription auditing), the same webhook URL accepts Event Grid's array-of-events format. No code change in the receiver — see [`../Event-Grid-Ingest.md`](../Event-Grid-Ingest.md) for the handshake details.

## Audit log API (admin events)

Service Hooks don't cover **org-level admin events** (project creation, permission changes, policy changes). For those, the ADO Audit Log API is the canonical source. Pulling that into ActivityLog is **not built today** — tracked as a future integration. For now, customers who need admin-event audit pull via Azure Function or scheduled script and POST to the webhook URL.

## Retries and idempotency

ADO retries failed webhook deliveries up to 25 times with exponential backoff. Our endpoint accepts the same `(systemId, sourceEventId)` collapse rule as native ingest, so retries land as no-ops (HTTP 201 returned with the original message id).

For ADO Service Hooks the canonical idempotency key is the event's `id` field (a GUID); our receiver derives `sourceEventId` from it automatically — you don't need to configure anything.

## Subscription health monitoring

ADO silently disables a Service Hook after repeated failures (10 consecutive). If you see events dry up, check **Project Settings → Service hooks → History** tab for the affected subscription and re-enable / re-test.

Operator-facing: the `DevOpsSubscriptionMonitor` (planned) automates the re-enable. Tracked in the DevOps SPEC.

## What's next

| Goal | Doc |
|---|---|
| Webhook endpoint behavior in detail | [`53-ingest-webhooks.md`](./53-ingest-webhooks.md) |
| Full ADO event catalogue + metadata schema | [`../../../ActivityLog-Integrations/DevOps/SPEC.md`](../../../ActivityLog-Integrations/DevOps/SPEC.md) |
| Cross-source query: ADO + Teams + Claude together | [`60-query-api.md`](./60-query-api.md) |

## Troubleshooting

**ADO subscription test shows 404 webhook-not-found.**
The token in the URL is wrong or revoked. Re-copy from the portal Systems → token detail page.

**Events stop appearing after a stretch of working fine.**
Check the Service Hook history in ADO — most likely the subscription was disabled after a transient outage. Re-enable and re-test.

**I want PR comment bodies and commit diffs.**
Not stored today (Mode 3 territory — we'd need to negotiate the body-cap and PII tradeoffs). If you have a use case, file a ticket.
