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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxEndpoints
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 1GitLab 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 1FAQ
- 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.