Documentation

CI/CD integration

Run apporig uniqueness analysis from any pipeline (GitHub Actions, GitLab CI, Bitbucket). Add one step that uploads your build, polls for a verdict, and fails the pipeline when the code is a copy of another project in your workspace.

How it works

Authenticate with a workspace API key, upload your project as a ZIP, then poll for the result. Each upload becomes a new revision of the project and is compared against your other projects and previous builds.

The verdict is derived from the headline overlap score: OK (under 40% overlap), RELATED (40–70%), or COPY (70% or higher). Uniqueness is 100 minus the overlap percentage.

  • OK — the project is sufficiently unique.
  • RELATED — meaningful overlap with another project; review recommended.
  • COPY — high overlap; fail the pipeline with failOnCopy=true.

Authentication

Create a key under Team Settings → API Keys. The full key is shown only once — store it as a CI secret (for example APPORIG_KEY). Pass it on every request in the Authorization header.

Keys are scoped to a single workspace and can only call the CI/CD API. Revoke a key at any time from the same settings page.

Authorization header

Authorization: Bearer ciu_xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Endpoints

Base URL: https://apporig.com/api/v1/ci

POST /upload — multipart upload of a ZIP archive. Fields: file (the archive), projectId (a stable project name; found-or-created in your workspace), and optional language (swift, objectivec, kotlin, java, reactnative, flutter). Returns { revisionId, projectId, status: "processing" }.

GET /status/{revisionId} — returns { status, uniquenessPercent, verdict, reportUrl }. status is processing, completed, or failed.

GET /result/{revisionId} — the full result including overallScore and a per-layer breakdown for parsing in your pipeline.

Upload a build

curl -X POST https://apporig.com/api/v1/ci/upload \
  -H "Authorization: Bearer $APPORIG_KEY" \
  -F "file=@build.zip" \
  -F "projectId=my-app" \
  -F "language=swift"

Check status

curl -H "Authorization: Bearer $APPORIG_KEY" \
  "https://apporig.com/api/v1/ci/status/<revisionId>?failOnCopy=true"

Failing the pipeline on COPY

Analysis runs asynchronously, so upload returns immediately with status "processing". Poll /status (or /result) until status is "completed".

Add failOnCopy=true to the /status or /result request: once analysis completes with a COPY verdict, the endpoint responds with HTTP 422 instead of 200. A non-zero exit on 422 fails the build. OK and RELATED return 200.

GitHub Actions

Add this step after your build. Store the key as the APPORIG_KEY repository secret.

.github/workflows/uniqueness.yml

- name: Check code uniqueness
  env:
    APPORIG_KEY: ${{ secrets.APPORIG_KEY }}
  run: |
    zip -r build.zip . -x '*.git*'
    REV=$(curl -sf -X POST https://apporig.com/api/v1/ci/upload \
      -H "Authorization: Bearer $APPORIG_KEY" \
      -F "file=@build.zip" \
      -F "projectId=${{ github.repository }}" \
      -F "language=swift" | jq -r .revisionId)
    echo "Revision: $REV"
    for i in $(seq 1 60); do
      CODE=$(curl -s -o body.json -w "%{http_code}" \
        -H "Authorization: Bearer $APPORIG_KEY" \
        "https://apporig.com/api/v1/ci/status/$REV?failOnCopy=true")
      STATUS=$(jq -r .status body.json)
      [ "$CODE" = "422" ] && { echo "COPY detected — failing build"; cat body.json; exit 1; }
      [ "$STATUS" = "completed" ] && { echo "Uniqueness OK"; cat body.json; exit 0; }
      [ "$STATUS" = "failed" ] && { echo "Analysis failed"; exit 1; }
      sleep 10
    done
    echo "Timed out waiting for analysis"; exit 1

GitLab CI

Define the APPORIG_KEY CI/CD variable, then add this job.

.gitlab-ci.yml

uniqueness:
  stage: test
  image: alpine:latest
  before_script:
    - apk add --no-cache curl jq zip
  script:
    - zip -r build.zip . -x '*.git*'
    - REV=$(curl -sf -X POST https://apporig.com/api/v1/ci/upload -H "Authorization: Bearer $APPORIG_KEY" -F "file=@build.zip" -F "projectId=$CI_PROJECT_PATH" -F "language=swift" | jq -r .revisionId)
    - |
      for i in $(seq 1 60); do
        CODE=$(curl -s -o body.json -w "%{http_code}" -H "Authorization: Bearer $APPORIG_KEY" "https://apporig.com/api/v1/ci/status/$REV?failOnCopy=true")
        STATUS=$(jq -r .status body.json)
        [ "$CODE" = "422" ] && { echo "COPY detected"; cat body.json; exit 1; }
        [ "$STATUS" = "completed" ] && { cat body.json; exit 0; }
        [ "$STATUS" = "failed" ] && { echo "Analysis failed"; exit 1; }
        sleep 10
      done
      exit 1

FAQ

What does projectId mean in the upload request?
It is a stable project name. The first upload creates the project in your workspace; later uploads with the same projectId add new revisions that are compared against previous builds.
How do I make the pipeline fail on a copy?
Poll GET /status or GET /result with failOnCopy=true. Once analysis completes with a COPY verdict, the endpoint returns HTTP 422, which a non-zero exit turns into a failed build.
Are there rate limits?
Yes. Upload and read endpoints are rate-limited per API key. If you exceed the limit you receive HTTP 429 with a Retry-After header.