Contents
Getting started
Using Codepanion
Connecting your tools
Codepanion indexes your source by receiving a gzipped tarball of your repo. You send it with a single curl call: no custom GitHub Actions, no CLI to install. You'll need an API key (or the keyless OIDC setup below); see getting your API key.
Creating the tarball
Pipe git ls-files into tar. This includes exactly the files tracked in your repo. Your .gitignore is the source of truth, so build artifacts and dependencies are excluded automatically.
# Tar everything git tracks. Respects .gitignore, no extra config needed
git ls-files -z --recurse-submodules | tar -czf repo.tar.gz --null -T -Tip: uncommitted changes won't be included, so commit (or stash to a branch) first. --recurse-submodules picks up submodule files; drop it if you only want the outer repo.
Adding context with .codepanion files
You can hand the agent extra context by committing .codepanion files anywhere in your repo: repo-wide (.codepanion), per-directory (src/payments/.codepanion), or per-file (src/payments/refund.ts.codepanion). They're indexed alongside your source, and the agent fetches the relevant ones on demand, so this is the place to record domain knowledge that isn't obvious from the code itself ("amounts are in cents," "Stripe for cards, Adyen for direct debit").
Sending the tarball
Post the gzipped archive to the ingest endpoint. Replace {tenantId} with your tenant ID: the identifier of your Codepanion account, and the only part of the URL that differs between customers.
curl -X POST https://api.codepanion.app/api/ingest/{tenantId} \
-H "Authorization: Bearer $CODEPANION_TOKEN" \
-H "Content-Type: application/gzip" \
-H "X-Commit-Sha: $COMMIT_SHA" \
--data-binary @repo.tar.gzWhere to find your tenant ID
Your tenant ID is a GUID that identifies your Codepanion account. It is not shown anywhere in the app today. If you don't have it, ask your Codepanion contact and we'll give it to you. The tenant ID alone grants nothing: an ingest call still has to present a valid API key (or a keyless OIDC token from an allow-listed repository), and a key only ever works for the account it was created in.
The API returns the snapshot ID and status; indexing then runs asynchronously (chunk → embed → store):
{
"snapshotId": 42,
"status": "pending",
"codeBase": "default",
"environment": null,
"name": null
}The snapshot moves pending → indexing → ready as the worker picks it up, and the agent only ever searches ready snapshots, so an upload in flight never leaves it answering from half a repository. A snapshot that fails to index is marked failed and the previous ready snapshot keeps serving.
An upload is capped at 2 MB compressed per account by default, 10 MB uncompressed, and 10,000 files. A push over any of those is refused with a 413 whose message names the cap it hit, and nothing is stored. All three are raised on request: tell us the size of your repository when you onboard and we set the account's limit to fit it.
Tag an upload with an environment label and a snapshot name to keep QA or staging separate from prod; see environments.
Multiple repositories (codebases)
One Codepanion tenant can index more than one repository. Each upload lands in a codebase; tell us which one with the X-CodeBase header (or a codebase query parameter):
curl -X POST "https://api.codepanion.app/api/ingest/$TENANT_ID" \
-H "Authorization: Bearer $CODEPANION_TOKEN" \
-H "Content-Type: application/gzip" \
-H "X-Commit-Sha: $COMMIT_SHA" \
-H "X-CodeBase: billing-service" \
--data-binary @repo.tar.gzCodebase names are lowercase letters, digits and hyphens (anything else is normalised, so acme/Widget API becomes acme-widget-api), and a codebase is created automatically the first time you ingest into it. Leave the header off and the upload goes to your default codebase, so single-repo setups need no configuration. With keyless OIDC ingest (below), each repository lands in a codebase named after it automatically. The agent searches across all your codebases by default and can scope an investigation to one.
Settings → Codebases lists what you've pushed to, with a snapshot count each, and lets you create one up front rather than waiting for the first ingest. A slug is permanent, because it's the address CI and the agent use, so only the display name can be renamed. Codebases can't be deleted, because their snapshots belong to them. Environment labels are independent of codebases: a qa label applies within whichever codebase you upload to.
CI example: GitHub Actions
A complete workflow that ingests on every push to main, using plain curl:
name: Ingest codebase
on:
push:
branches: [main]
jobs:
ingest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create tarball
run: git ls-files -z --recurse-submodules | tar -czf repo.tar.gz --null -T -
- name: Send to Codepanion
env:
CODEPANION_TOKEN: ${{ secrets.CODEPANION_TOKEN }}
TENANT_ID: ${{ vars.CODEPANION_TENANT_ID }}
run: |
curl -X POST "https://api.codepanion.app/api/ingest/$TENANT_ID" \
-H "Authorization: Bearer $CODEPANION_TOKEN" \
-H "Content-Type: application/gzip" \
-H "X-Commit-Sha: $GITHUB_SHA" \
--data-binary @repo.tar.gz \
--fail --silent --show-errorStore CODEPANION_TOKEN as a repository secret and CODEPANION_TENANT_ID as a repository variable.
Keyless ingest (recommended): GitHub Actions OIDC
GitHub's OpenID Connect removes the stored key. Your workflow proves its identity per run; Codepanion validates that short-lived token and checks the repository against an allowlist you manage in Settings → Keyless CI. This also removes API key rotation from the workflow.
name: Ingest codebase
on:
push:
branches: [main]
permissions:
id-token: write # mint the short-lived OIDC token
contents: read
jobs:
ingest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: git ls-files -z --recurse-submodules | tar -czf repo.tar.gz --null -T -
- name: Send to Codepanion (keyless)
env:
TENANT_ID: ${{ vars.CODEPANION_TENANT_ID }}
run: |
TOKEN=$(curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=codepanion-ingest" | jq -r .value)
curl -X POST "https://api.codepanion.app/api/ingest/$TENANT_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/gzip" \
-H "X-Commit-Sha: $GITHUB_SHA" \
--data-binary @repo.tar.gz --fail --silent --show-errorAdd your-org/your-repo to the allowlist in Settings → Keyless CI. Keyless ingest stays off until at least one repository is listed. No repository secret needed, only the tenant ID variable.
The first accepted push binds that allowlist entry to the repository's GitHub ID, and later pushes have to come from the same repository. An owner/repo name can be renamed, transferred, or released and claimed by someone else, so the name on its own is treated as a label. If you rename or transfer a repository, remove the old entry and add the new name.