Webhook · One endpoint · Any CI provider

Wire your CI into one release cockpit.

POST queued, running, and terminal events from Bitrise, Codemagic, GitHub Actions, Xcode Cloud, or any shell script. Retries update the same provider run instead of creating duplicates.

The endpoint

POST https://simpleappshipper.com/api/ci/build-completed
Content-Type: application/json
X-CI-Webhook-Token: <your shared secret>
X-Device-ID: <device id from your Mac app>       # legacy device-scoped flow
Authorization: Bearer <sas_session_token>        # signed-in flow

# Supply Authorization or X-Device-ID. Linking a release workspace requires Authorization.

Request body

FieldTypeNotes
projectrequiredRepo name or Xcode project basename. Used to group builds.
provider_run_idrequiredStable provider run identifier. The idempotency key is account/device + provider + project + this value.
provider_event_idoptionalProvider event/delivery identifier retained for diagnostics.
branchoptionalGit branch name.
commit_shaoptionalFull or short SHA. Used for diffing in the Mac app.
version_stringoptionale.g. "2.4.0".
build_numberoptionale.g. "47".
artifact_urloptionalHTTPS URL to the .ipa / .pkg / .app. R2, S3, GitHub artifact, etc.
artifact_checksum_sha256optionalExactly 64 hexadecimal characters. Returned as evidence; not fetched or verified server-side.
artifact_kindoptionalipa (default) · pkg · app
platformoptionalios (default) · macos · appletvos · visionos
ci_provideroptionalbitrise · codemagic · github · xcode-cloud · manual · other
ci_run_urloptionalLink back to the CI run for diagnostics.
statusoptionalqueued · running · succeeded · failed · cancelled. Missing or legacy completed normalizes to succeeded; other values are rejected.
queued_at, started_at, completed_atoptionalISO-8601 lifecycle timestamps. A matching timestamp is filled when its event arrives without one.
duration_secondsoptionalNon-negative integer, up to 31 days. Derived when start and completion timestamps are both present.
testsoptionalObject with non-negative integer total, failed, and skipped counts.
release_workspace_idoptionalRequires a valid JWT and owner, release manager, or editor membership in that workspace.
metadataoptionalJSON object or array, bounded to 4 KB. Oversized metadata is discarded; secret, token, password, private-key, authorization, and command keys are rejected.

Response

200 OK
{
  "id": "01HV9X…",
  "accepted": true,
  "idempotent": false
}

List lifecycle records

The authenticated list endpoint uses a strict integer limit from 1–100 and an opaque cursor returned by the preceding page. It returns lifecycle timestamps, duration, test totals, checksum, release workspace link, and bounded metadata.

GET https://simpleappshipper.com/api/ci/builds?project=MyApp&limit=20&cursor=<next_cursor>
Authorization: Bearer <sas_session_token>

200 OK
{
  "builds": [
    {
      "provider_run_id": "run-42",
      "status": "succeeded",
      "duration_seconds": 120,
      "tests": { "total": 20, "failed": 0, "skipped": 1 },
      "artifact_checksum_sha256": "…"
    }
  ],
  "next_cursor": null
}

Provider snippets

Bitrise (bash step)

#!/usr/bin/env bash
curl -fsS https://simpleappshipper.com/api/ci/build-completed \
  -H "Content-Type: application/json" \
  -H "X-CI-Webhook-Token: $SAS_WEBHOOK_TOKEN" \
  -H "X-Device-ID: $SAS_DEVICE_ID" \
  -d "{
    \"project\": \"$BITRISE_APP_TITLE\",
    \"provider_run_id\": \"$BITRISE_BUILD_SLUG\",
    \"provider_event_id\": \"$BITRISE_BUILD_SLUG-completed\",
    \"status\": \"succeeded\",
    \"branch\": \"$BITRISE_GIT_BRANCH\",
    \"commit_sha\": \"$BITRISE_GIT_COMMIT\",
    \"version_string\": \"$BITRISE_BUILD_VERSION\",
    \"build_number\": \"$BITRISE_BUILD_NUMBER\",
    \"artifact_url\": \"$BITRISE_PUBLIC_INSTALL_PAGE_URL\",
    \"ci_provider\": \"bitrise\",
    \"ci_run_url\": \"$BITRISE_BUILD_URL\"
  }"

Codemagic (codemagic.yaml)

scripts:
  - name: Notify Simple App Shipper
    script: |
      curl -fsS https://simpleappshipper.com/api/ci/build-completed \
        -H "Content-Type: application/json" \
        -H "X-CI-Webhook-Token: $SAS_WEBHOOK_TOKEN" \
        -H "X-Device-ID: $SAS_DEVICE_ID" \
        -d "{
          \"project\": \"$CM_PROJECT_NAME\",
          \"provider_run_id\": \"$CM_BUILD_ID\",
          \"provider_event_id\": \"$CM_BUILD_ID-completed\",
          \"status\": \"succeeded\",
          \"branch\": \"$CM_BRANCH\",
          \"commit_sha\": \"$CM_COMMIT\",
          \"version_string\": \"$CM_BUILD_VERSION\",
          \"build_number\": \"$CM_BUILD_NUMBER\",
          \"ci_provider\": \"codemagic\",
          \"ci_run_url\": \"$CM_BUILD_URL\"
        }"

GitHub Actions (.github/workflows/ios.yml)

- name: Notify Simple App Shipper
  if: success()
  env:
    SAS_WEBHOOK_TOKEN: ${{ '{{' }} secrets.SAS_WEBHOOK_TOKEN {{ '}}' }}
    SAS_DEVICE_ID:     ${{ '{{' }} secrets.SAS_DEVICE_ID {{ '}}' }}
  run: |
    curl -fsS https://simpleappshipper.com/api/ci/build-completed \
      -H "Content-Type: application/json" \
      -H "X-CI-Webhook-Token: $SAS_WEBHOOK_TOKEN" \
      -H "X-Device-ID: $SAS_DEVICE_ID" \
      -d "{
        \"project\": \"${{ '{{' }} github.event.repository.name {{ '}}' }}\",
        \"provider_run_id\": \"${{ '{{' }} github.run_id {{ '}}' }}-${{ '{{' }} github.run_attempt {{ '}}' }}\",
        \"provider_event_id\": \"${{ '{{' }} github.run_id {{ '}}' }}-${{ '{{' }} github.run_attempt {{ '}}' }}-completed\",
        \"status\": \"succeeded\",
        \"branch\": \"${{ '{{' }} github.ref_name {{ '}}' }}\",
        \"commit_sha\": \"${{ '{{' }} github.sha {{ '}}' }}\",
        \"ci_provider\": \"github\",
        \"ci_run_url\": \"${{ '{{' }} github.server_url {{ '}}' }}/${{ '{{' }} github.repository {{ '}}' }}/actions/runs/${{ '{{' }} github.run_id {{ '}}' }}\"
      }"

Xcode Cloud (post-build script)

Add a custom script under ci_post_xcodebuild.sh:

#!/usr/bin/env bash
curl -fsS https://simpleappshipper.com/api/ci/build-completed \
  -H "Content-Type: application/json" \
  -H "X-CI-Webhook-Token: $SAS_WEBHOOK_TOKEN" \
  -H "X-Device-ID: $SAS_DEVICE_ID" \
  -d "{
    \"project\": \"$CI_PRODUCT\",
    \"provider_run_id\": \"$CI_BUILD_ID\",
    \"provider_event_id\": \"$CI_BUILD_ID-completed\",
    \"status\": \"succeeded\",
    \"branch\": \"$CI_BRANCH\",
    \"commit_sha\": \"$CI_COMMIT\",
    \"version_string\": \"$CI_BUILD_NUMBER\",
    \"ci_provider\": \"xcode-cloud\",
    \"ci_run_url\": \"$CI_BUILD_URL\"
  }"

Anything else (curl)

The webhook is a plain HTTPS POST. Any tool that can provide a stable provider_run_id and run curl can wire to it.

What the Mac app does with these

The CI Builds panel lists recent runs grouped by project, with provider, branch, version/build, commit, status, and links to the CI run or artifact when supplied. The ingestion service stores metadata only: it never downloads an artifact, starts an upload, invokes provider commands, or submits an app to App Store Connect.

Security