> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lanesync.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# CI evidence upload

> Upload test evidence from GitHub Actions to LaneSync — OIDC authentication, API payload, artifacts, and SDLC_API_URL configuration.

CI pipelines upload test evidence to LaneSync so the **EVIDENCING** stage can verify artifacts and power quality scorecards. Bootstrapped workflow templates include authenticated upload out of the box.

## Overview

```mermaid theme={null}
sequenceDiagram
  participant GHA as GitHub Actions
  participant LS as LaneSync API
  participant DB as Database

  GHA->>GHA: Mint OIDC token (id-token: write)
  GHA->>GHA: Run tests and generate reports
  GHA->>LS: POST /api/evidence/upload + Authorization Bearer
  LS->>LS: Verify OIDC JWT or API key
  LS->>DB: Store evidence (tenant from token, not body)
  LS-->>GHA: 200 evidenceId
```

Evidence ingestion **requires CI authentication**. Tenant is derived from the verified GitHub OIDC token (`repository_owner` claim) or from a scoped **LaneSync API key** — never from an unauthenticated `org` body field.

## Authentication

### GitHub Actions OIDC (recommended)

Bootstrapped workflows request an OIDC token with audience `lanesync`:

```yaml theme={null}
permissions:
  id-token: write
  contents: read

- name: Upload evidence to LaneSync
  run: |
    TOKEN=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
      "${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=lanesync" | jq -r .value)
    curl -X POST "$SDLC_API_URL/api/evidence/upload" \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer $TOKEN" \
      -d "$PAYLOAD"
```

No long-lived secrets required — the token is short-lived and cryptographically bound to the repository.

### LaneSync API key (fallback)

For non-GitHub CI or manual scripts:

1. Workspace admin creates a key in **Settings → System → CI API Keys**
2. Store as repository secret `LANESYNC_API_KEY`
3. Send `Authorization: Bearer lsk_…` on upload requests

When using an API key, include `org` in the JSON body (must match the key's workspace).

<Note>
  Bootstrapped workflows try `LANESYNC_API_KEY` first, then fall back to OIDC automatically.
</Note>

## Bootstrapped workflow

LaneSync pushes CI templates from `templates/.github/workflows/` during bootstrap. The upload step runs on release branch pushes with OIDC auth configured.

<Note>
  Bootstrapped workflows default `SDLC_API_URL` to `https://lanesync.dev`. Self-hosted customers set the `SDLC_API_URL` repository variable. See [Enterprise self-hosting](/enterprise/self-hosting).
</Note>

## POST /api/evidence/upload

**Auth:** `Authorization: Bearer <OIDC JWT | lsk_* API key>`

**Required fields:** `org`, `repo`, `version`, `pipeline`, `metrics`

```json theme={null}
{
  "org": "acme",
  "repo": "api",
  "version": "v1.0.0",
  "pipeline": "CI — Build & Test",
  "metrics": {
    "unit_tests": { "passed": 120, "failed": 0, "skipped": 2 },
    "coverage": { "line": 91.2, "branch": 84.0 },
    "sast_scan": { "critical": 0, "high": 1 },
    "security": { "cves": 0 }
  },
  "reports": {},
  "environment": "staging"
}
```

**Response:**

```json theme={null}
{ "evidenceId": "uuid", "message": "Evidence uploaded successfully" }
```

| Status | Condition                                  |
| ------ | ------------------------------------------ |
| `200`  | Success                                    |
| `400`  | Missing required fields                    |
| `401`  | Missing or invalid CI credentials          |
| `403`  | `org`/`repo` does not match token identity |
| `404`  | Org not installed or milestone not found   |

## Upload test reports

**POST /api/evidence/upload-report** — multipart form, file field `report`. Same CI auth required.

LaneSync parses JUnit XML and links test cases to features per [Quality tracking](/concepts/quality-tracking).

## Required artifacts

Configure artifact names in `sdlc.yaml`:

```yaml theme={null}
evidencing:
  required_artifacts:
    - test-results
    - backend-test-results
```

Bootstrapped CI produces these HTML reports even on empty repos (placeholder content) so day-zero bootstrap never blocks:

* `unit-tests.html`
* `integration-tests.html`
* `coverage/index.html`
* `sast-scan-report.html`
* `cve-scan.html`
* `sdlc-evidence.json`

<Warning>
  CI template steps after tests use `if: always()` so artifacts upload even when tests fail — required for evidencing and audit.
</Warning>

## Custom pipelines

If you maintain your own workflow instead of the LaneSync template, see the full step-by-step guide:

<Card title="Add LaneSync to an existing CI pipeline" icon="wrench" href="/guides/add-lanesync-to-existing-ci">
  Copy-paste blocks for unit tests, SAST, CVE scanning, coverage, and multi-environment evidence upload — dev, staging, and production.
</Card>

At minimum you need to:

1. Add `permissions: id-token: write` to the upload job
2. Mint an OIDC token with audience `lanesync`, or use a LaneSync API key
3. Match `pipeline` name to a check in `sdlc.yaml` if needed
4. POST metrics after test jobs complete with `Authorization: Bearer …`
5. Set `SDLC_API_URL` for non-hosted deployments
6. Send `environment: dev | staging | production` and a matching `version` in the payload so evidence is stored per-environment

## Troubleshooting

| Issue                            | Fix                                                                     |
| -------------------------------- | ----------------------------------------------------------------------- |
| `401` CI authentication required | Add OIDC token or `LANESYNC_API_KEY` secret                             |
| `403` org/repo mismatch          | Ensure body `org`/`repo` match the GitHub repo running the workflow     |
| `404` org not found              | Verify GitHub App installed on org                                      |
| `404` milestone not found        | Create milestone with matching version string                           |
| Reports not parsed               | Check file format (JUnit XML); malformed files are skipped with warning |
| Evidence not showing             | Confirm release branch push triggered upload step                       |

See [API Reference](/api-reference/introduction) for full endpoint documentation.
