GitHub Action contract signing

Engineering-led teams want CI to produce a contract artifact, send it for signature, and gate a release on envelope.signed. Atlas API plus GitHub Actions HTTP steps or a small composite action covers the flow.

> Share: "Workflow uploads PDF, Atlas returns review_url. Protected environment waits for webhook or poll."

Workflow shape

build job → upload contract PDF artifact
sign job → POST /api/envelope → output review_url
manual approval (GitHub environment) → human Send on review
wait job → poll status or webhook to repository_dispatch
release job → proceeds when signed

Treat signing as an async human step. Do not expect fully unattended sign on first-run PDFs unless templates are pre-approved.

Action step: create envelope

name: Send vendor MSA

on:
  workflow_dispatch:
    inputs:
      signer_email:
        required: true
        type: string

jobs:
  send:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Create Atlas envelope
        id: atlas
        env:
          ATLAS_API_KEY: ${{ secrets.ATLAS_API_KEY }}
        run: |
          RESP=$(curl -sS -X POST https://atlaswork.ai/api/envelope \
            -H "Authorization: Bearer $ATLAS_API_KEY" \
            -H "Content-Type: application/json" \
            -H "Idempotency-Key: gh-${{ github.run_id }}-create" \
            -d "{
              \"document_url\": \"https://releases.example.com/${{ github.sha }}/vendor-msa.pdf\",
              \"webhook_url\": \"https://your-app.example/hooks/atlas\",
              \"metadata\": {
                \"client_reference_id\": \"gh-${{ github.repository }}-${{ github.run_id }}\",
                \"external_id\": \"${{ github.run_id }}\"
              },
              \"parties\": [{
                \"email\": \"${{ inputs.signer_email }}\",
                \"name\": \"Vendor Signer\",
                \"role\": \"Vendor\"
              }]
            }")
          echo "envelope_id=$(echo $RESP | jq -r .envelope_id)" >> $GITHUB_OUTPUT
          echo "review_url=$(echo $RESP | jq -r .review_url)" >> $GITHUB_OUTPUT

      - name: Comment review URL
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: `Review and send: ${{ steps.atlas.outputs.review_url }}`
            })

Adapt comment step if not running from an issue context. Use Job Summary instead:

- run: echo "### Review URL\n${{ steps.atlas.outputs.review_url }}" >> $GITHUB_STEP_SUMMARY

Wait for signed status

Poll job (simple but noisy):

wait-signed:
  needs: send
  runs-on: ubuntu-latest
  steps:
    - env:
        ATLAS_API_KEY: ${{ secrets.ATLAS_API_KEY }}
        ENVELOPE_ID: ${{ needs.send.outputs.envelope_id }}
      run: |
        for i in $(seq 1 60); do
          STATUS=$(curl -sS -H "Authorization: Bearer $ATLAS_API_KEY" \
            "https://atlaswork.ai/api/envelope/$ENVELOPE_ID/status" | jq -r .status)
          if [ "$STATUS" = "signed" ]; then exit 0; fi
          sleep 300
        done
        exit 1

Better: middleware receives Atlas webhook and calls repository_dispatch with event_type: atlas-signed to resume workflow.

Secrets hygiene

  • ATLAS_API_KEY in GitHub Actions secrets, environment-scoped for production
  • Never log full API key or review tokens
  • Idempotency-Key includes github.run_id to survive Action retries

CI/CD webhook keyword overlap

Teams search "ci cd contract signing webhook" for this pattern. The webhook is the release gate signal, not GitHub's native signature.

Template path for repeat vendor MSAs

After first manual review, save Atlas template. Workflow calls POST /api/templates/{id}/send with version tag prefill from github.ref_name.

Branch protection integration

Some teams require signed vendor MSAs before merging to main. Combine this workflow with GitHub environment protection rules: the wait-signed job becomes a required check once webhook or poll succeeds.

Artifact retention

Store signed PDF as GitHub Actions artifact for 90 days and upload to permanent storage via webhook handler. Release tags should reference envelope id in release notes for audit trail.

Monorepo matrix

Use strategy matrix over vendor/**/msa.pdf paths so one workflow dispatches multiple envelopes without copy-pasting YAML blocks per vendor folder.

Secrets rotation

Rotate ATLAS_API_KEY on a calendar schedule. GitHub Actions secrets update once; workflows pick up new value without YAML edits. Document rotation in SECURITY.md so contractors know not to hardcode keys in workflow files.

OIDC note

GitHub OIDC can authenticate to AWS for artifact upload but Atlas integration still uses Bearer API keys today. Do not confuse cloud OIDC with Atlas MCP OAuth, which targets chat clients.

FAQ

Can GitHub sign the PDF itself? GitHub does not e-sign contracts. Atlas handles ceremony.

OIDC to Atlas? Atlas API uses Bearer keys. OIDC is for MCP OAuth clients, not GitHub Actions.

402 on send? Human clicking Send needs credits. Fund billing before running workflow on production templates.

DOCX artifacts? Upload PDF or DOCX URL. DOCX converts at create.

Monorepo multiple contracts? Matrix strategy with one envelope per artifact path.

Extended FAQ

GitHub Enterprise Server? Same Actions YAML if outbound HTTPS to Atlas is allowed.

Required reviewers on sign job? Use environment protection with manual approval before wait-signed job.

Fork PRs from forks? Do not run sign workflows on untrusted forks with production secrets.

OIDC to Atlas? Not supported. Use repository secrets for API key.

Release asset vs webhook PDF? Webhook PDF is legally authoritative. Artifact is convenience copy.

Security hardening

Restrict ATLAS_API_KEY to protected branches and environments. Forked pull requests from external contributors must not receive secrets. Use if: github.repository_owner == 'yourorg' guard on sign workflows until trust model is documented.

API reference

Full route list and request schemas live at /dev. Start with E-signature API for the mental model, then use this guide for copy-paste examples.