# Keep the docs in sync

Publish a new version of your OpenAPI file to Confluence from GitHub Actions, GitLab CI, Bitbucket Pipelines or Azure DevOps.

Apifolio always shows the **latest version** of the attached file. To keep the docs in sync with your code, upload the spec as a new version of the same attachment from your CI pipeline. Confluence keeps every version, and the page updates by itself.

## What you need

- the **page ID** of the page that holds the spec (the number in the page URL, `/pages/123456/...`);
- an **Atlassian API token** for a user who can edit that page, created at [id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens). Store it as a secret in your CI, never in the repository;
- the **file name** used by the macro, for example `openapi.yaml`. Keep the same name so the macro finds the new version.

## The request

```bash
curl --fail -sS -u "$CONFLUENCE_EMAIL:$CONFLUENCE_API_TOKEN" \
  -X PUT \
  -H "X-Atlassian-Token: no-check" \
  -F "file=@openapi.yaml" \
  -F "minorEdit=true" \
  -F "comment=Updated from CI ($GIT_COMMIT)" \
  "https://your-site.atlassian.net/wiki/rest/api/content/$PAGE_ID/child/attachment"
```

`PUT` creates the attachment the first time and adds a new version afterwards. `minorEdit=true` avoids notifying page watchers on every build.

## GitHub Actions

```yaml
name: Publish API docs
on:
  push:
    branches: [main]
    paths: [openapi.yaml]
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Upload openapi.yaml to Confluence
        env:
          CONFLUENCE_EMAIL: ${{ secrets.CONFLUENCE_EMAIL }}
          CONFLUENCE_API_TOKEN: ${{ secrets.CONFLUENCE_API_TOKEN }}
          PAGE_ID: "123456"
        run: |
          curl --fail -sS -u "$CONFLUENCE_EMAIL:$CONFLUENCE_API_TOKEN" -X PUT \
            -H "X-Atlassian-Token: no-check" \
            -F "file=@openapi.yaml" -F "minorEdit=true" \
            -F "comment=Updated from ${GITHUB_SHA::7}" \
            "https://your-site.atlassian.net/wiki/rest/api/content/$PAGE_ID/child/attachment"
```

## GitLab CI

```yaml
publish-api-docs:
  image: curlimages/curl:latest
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      changes: [openapi.yaml]
  script:
    - >
      curl --fail -sS -u "$CONFLUENCE_EMAIL:$CONFLUENCE_API_TOKEN" -X PUT
      -H "X-Atlassian-Token: no-check"
      -F "file=@openapi.yaml" -F "minorEdit=true"
      -F "comment=Updated from $CI_COMMIT_SHORT_SHA"
      "https://your-site.atlassian.net/wiki/rest/api/content/$PAGE_ID/child/attachment"
```

## Bitbucket Pipelines

```yaml
pipelines:
  branches:
    main:
      - step:
          name: Publish API docs
          image: curlimages/curl:latest
          script:
            - curl --fail -sS -u "$CONFLUENCE_EMAIL:$CONFLUENCE_API_TOKEN" -X PUT -H "X-Atlassian-Token: no-check" -F "file=@openapi.yaml" -F "minorEdit=true" "https://your-site.atlassian.net/wiki/rest/api/content/$PAGE_ID/child/attachment"
```

## Azure DevOps

```yaml
steps:
  - script: |
      curl --fail -sS -u "$(CONFLUENCE_EMAIL):$(CONFLUENCE_API_TOKEN)" -X PUT \
        -H "X-Atlassian-Token: no-check" \
        -F "file=@openapi.yaml" -F "minorEdit=true" \
        "https://your-site.atlassian.net/wiki/rest/api/content/$(PAGE_ID)/child/attachment"
    displayName: Publish API docs to Confluence
```

After a new version is uploaded, open the macro configuration and click **Save** once in a while to refresh the [Confluence search](/docs/confluence-search/) digest.
