Enterprise Data API
Read your CodeDD audit and portfolio data from your own BI tools, warehouses, and reporting pipelines
Enterprise Data API
The Enterprise Data API gives you programmatic, read-only access to everything CodeDD has analysed for your firm — repository audit results, findings, dependencies, architecture, and portfolio-level KPIs — so you can build your own dashboards, feed a warehouse, or attach CodeDD numbers to an investment committee pack.
It is a REST API over HTTPS returning JSON, authenticated with an organization-scoped API key. Every endpoint is a GET; nothing in this API can change your data.
Availability: the CodeDD Enterprise plan. Portfolio Monitoring-only contracts do not include it.
Quick start
# 1. Store your key outside your shell history
export CODEDD_API_KEY="codedd_ent_..."
# 2. Ask the API what your key can do
curl -H "Authorization: Bearer $CODEDD_API_KEY" \
https://api.codedd.ai/api/v1/enterprise/
# 3. List the portfolio companies the key covers
curl -H "Authorization: Bearer $CODEDD_API_KEY" \
https://api.codedd.ai/api/v1/enterprise/portcos/
Creating a key
Keys are created by an administrator of your legal organization in Account → Enterprise plan → Data API keys.
- Click Create API key.
- Give it the name of the tool that will use it —
Power BI production, nottest— so you know what breaks if you revoke it. - Choose the scopes it needs (see below).
- Choose a lifetime. Every key expires; the maximum is 365 days.
- Copy the key immediately. It is displayed once and cannot be retrieved again.
A key belongs to the organization, not to the person who created it, so an integration keeps working when that person changes role or leaves. Up to 10 active keys per organization.
Scopes
Grant only what the consuming tool needs.
| Scope | Grants |
|---|---|
read:audits | Discovery (PortCos, group audits, repository list), PortCo settings, and all single-audit data endpoints |
read:portfolio | All group-audit roll-ups: KPIs, technical debt, benchmark, estate map, executive summary, category breakdown, development, security findings, DORA, AI-Native, financials, repo activity, supply chain, integration assessment, KPI history |
read:audits does not imply read:portfolio. A pipeline that only needs per-repository findings should not be able to read the firm-level roll-ups you would present to an investment committee.
If you lose a key
There is no reveal endpoint, by design: a value that can be fetched again can be fetched by the wrong person. Rotate the key instead. Rotation issues a replacement immediately and keeps the old key working for 24 hours, so you can roll the secret forward on your next deploy rather than at the same instant.
Authentication
Send the key as a bearer token on every request:
Authorization: Bearer codedd_ent_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The key must not appear in a URL, a query string, or a client-side bundle. It reads your firm's entire audit estate; treat it like a database password and store it in your secret manager.
Navigating your data
You never have to construct an identifier. Start from the key and walk down:
GET /api/v1/enterprise/ → what this key can do
GET /api/v1/enterprise/portcos/ → your portfolio companies
GET /api/v1/enterprise/portcos/{portco_uuid}/group-audits/ → audits run for one company
GET /api/v1/enterprise/group-audits/{group_audit_uuid}/audits/ → repositories in one audit
GET /api/v1/enterprise/audits/{audit_uuid}/scores/ → results for one repository
PortCo endpoints
No scope beyond org membership (any valid key for the firm).
| Endpoint | Returns |
|---|---|
/api/v1/enterprise/portcos/{portco_uuid}/settings/ | Financial assumptions (hourly_rate_usd, quality_threshold, target_coverage, …) |
/api/v1/enterprise/portcos/{portco_uuid}/kpi-history/ | Executive KPI values across group audits (?group_audit_uuids=uuid1,uuid2, optional — defaults to the 25 most recent) |
Repository audit endpoints
All require read:audits.
| Endpoint | Returns |
|---|---|
/api/v1/enterprise/audits/{audit_uuid}/ | Name, status, mode, lines of code, file count, synthesis date |
/api/v1/enterprise/audits/{audit_uuid}/scores/ | Quality scores across all assessed categories |
/api/v1/enterprise/audits/{audit_uuid}/summaries/ | Full narrative summaries — executive, security, code quality, performance, recommendations |
/api/v1/enterprise/audits/{audit_uuid}/flags/ | Every flag raised, with severity and location |
/api/v1/enterprise/audits/{audit_uuid}/dependencies/ | Packages, licences, and vulnerabilities |
/api/v1/enterprise/audits/{audit_uuid}/architecture/ | Detected technologies, components, and relationships |
/api/v1/enterprise/audits/{audit_uuid}/development/ | Git-derived development and contribution metrics |
/api/v1/enterprise/audits/{audit_uuid}/files/ | File statistics, extension mix, and test coverage |
/api/v1/enterprise/audits/{audit_uuid}/executive/ | Executive dashboard payload (?time_range=month|quarter|year|all) |
/api/v1/enterprise/audits/{audit_uuid}/complexity/ | Cyclomatic complexity and Halstead grade distribution |
/api/v1/enterprise/audits/{audit_uuid}/tier-benchmark/ | Tier-matched peer cohort comparison |
/api/v1/enterprise/audits/{audit_uuid}/ai-authorship/ | Human vs AI authorship attribution |
Unlike the in-app AI advisor, these return complete payloads — no truncated narratives, no "top 5 findings only".
Portfolio endpoints
All require read:portfolio and are addressed by group audit; CodeDD derives the portfolio company for you.
| Endpoint | Returns |
|---|---|
/api/v1/enterprise/group-audits/{group_audit_uuid}/kpis/{kpi_name}/ | One KPI: technical-debt, key-person, innovation, ip-security, scalability |
/api/v1/enterprise/group-audits/{group_audit_uuid}/technical-debt/ | Full technical debt dashboard with financial impact |
/api/v1/enterprise/group-audits/{group_audit_uuid}/benchmark/ | Benchmark comparison |
/api/v1/enterprise/group-audits/{group_audit_uuid}/estate-map/ | Estate map of repositories and technologies |
/api/v1/enterprise/group-audits/{group_audit_uuid}/executive-summary/ | Portfolio-level narrative |
/api/v1/enterprise/group-audits/{group_audit_uuid}/category-breakdown/ | Subcategory score breakdown (?category=quality) |
/api/v1/enterprise/group-audits/{group_audit_uuid}/development/ | Portfolio development overview |
/api/v1/enterprise/group-audits/{group_audit_uuid}/security-findings/ | Issue Compass findings (paginated) |
/api/v1/enterprise/group-audits/{group_audit_uuid}/dora/ | DORA four-key metrics and trends |
/api/v1/enterprise/group-audits/{group_audit_uuid}/ai-native/ | AI-Native portfolio assessment |
/api/v1/enterprise/group-audits/{group_audit_uuid}/financials/ | Dollar impact modeling (?key_person_scope=audited|material|root) |
/api/v1/enterprise/group-audits/{group_audit_uuid}/repo-activity/ | Repository activity compass |
/api/v1/enterprise/group-audits/{group_audit_uuid}/supply-chain/ | Package-level license and vulnerability drilldown |
/api/v1/enterprise/group-audits/{group_audit_uuid}/integration-assessment/ | M&A/PMI language × domain assessment |
Coverage boundary
The API exposes the audit results and portfolio roll-ups that reporting tools need. It does not mirror every dashboard screen — some surfaces are operational, write-only, or intentionally excluded.
| Dashboard area | Available via API | Not exposed (by design) |
|---|---|---|
| PortCo navigation | /portcos/, settings, KPI history | Member management, DORA OAuth config, remediation toggles |
| Group audit discovery | Metadata, member audits | Pipeline progress, invitation workflow, scope selection |
| Overview / Benchmark | KPIs, benchmark, executive summary, category breakdown, financials | Comparison matrix UI config |
| Architecture | Estate map | Estate curation writes |
| Development Activity | Development overview, repo activity, DORA, integration assessment | Innovation commits CSV export |
| Debt & Security | Technical debt, security findings, supply chain | Saved Issue Compass selections, finding detail drawer |
| AI-Native | AI-Native assessment, per-repo AI authorship | AI delivery funnel |
| Single audit — Summary | Scores, summaries, executive, files (aggregates) | Per-file tree, file search |
| Single audit — Files | Statistics, extension mix, test coverage, complexity | File treetable, file detail drawer |
| Remediation | — | Remediation dashboard (module-gated) |
| Admin / ops | — | Audit logs, comparable audits workflow |
If you need a dataset that is not listed above, contact your account manager — new endpoints are added when a reporting use case is clear and the underlying data is stable.
Response format
Every successful response uses the same envelope:
{
"status": "success",
"data": { "portcos": [ { "portco_uuid": "...", "name": "Acme GmbH" } ] },
"meta": {
"api_version": "1",
"correlation_id": "3f2b...",
"next_cursor": null,
"total_count": 12
}
}
Quote the correlation_id when contacting support — it identifies the exact request in our logs.
Pagination
List endpoints are paginated. Pass limit (default 50, maximum 100) and follow meta.next_cursor until it is null:
import requests
session = requests.Session()
session.headers['Authorization'] = f'Bearer {API_KEY}'
BASE = 'https://api.codedd.ai/api/v1/enterprise'
def fetch_all(path, key):
"""Every page of a list endpoint."""
items, cursor = [], None
while True:
params = {'limit': 100}
if cursor:
params['cursor'] = cursor
payload = session.get(f'{BASE}{path}', params=params, timeout=30).json()
items.extend(payload['data'][key])
cursor = payload['meta']['next_cursor']
if not cursor:
return items
portcos = fetch_all('/portcos/', 'portcos')
Treat the cursor as opaque. It is not an offset, and building your own would break the moment ordering changes.
Caching with ETags
Audit results are written once and then do not change, so repeat reads should be conditional. Repository endpoints return an ETag; send it back as If-None-Match and a 304 Not Modified costs you nothing:
curl -H "Authorization: Bearer $CODEDD_API_KEY" \
-H 'If-None-Match: "a1b2c3d4..."' \
https://api.codedd.ai/api/v1/enterprise/audits/$AUDIT/scores/
An audit still being analysed has no ETag, because its data is still moving.
Errors
Failures use a stable error_code you can branch on. Messages may be reworded; codes will not change within v1.
| HTTP | error_code | Meaning and what to do |
|---|---|---|
| 400 | invalid_parameter | A parameter failed validation. Fix the request. |
| 401 | invalid_api_key | Missing, malformed, or unknown key. Check the header. |
| 401 | api_key_expired | Past its expiry. Rotate it. |
| 401 | api_key_revoked | Revoked by an administrator. Ask for a new one. |
| 403 | insufficient_scope | The key lacks the scope this endpoint needs. Mint a key with it. |
| 403 | enterprise_plan_required | No active Enterprise plan on the organization. |
| 404 | resource_not_found | The resource does not exist or is not covered by your key. These are deliberately indistinguishable. |
| 409 | audit_in_progress | The audit has not finished, so this result set does not exist yet. Retry later. |
| 429 | rate_limit_exceeded | Honour the Retry-After header. |
| 500 | internal_error | Our fault. Retry with backoff; quote the correlation_id. |
{
"status": "error",
"error_code": "insufficient_scope",
"message": "This API key does not have the \"read:portfolio\" scope.",
"correlation_id": "9c1f..."
}
Rate limits
| Bucket | Limit |
|---|---|
| Standard endpoints, per key | 120 requests/minute |
| Heavy endpoints (findings, dependencies, files, all portfolio aggregates), per key | 30 requests/minute |
| All endpoints, per organization | 600 requests/minute |
Limits are per key, not per IP address, so one pipeline cannot throttle another that happens to share an egress address. On a 429, wait for Retry-After seconds; do not retry immediately.
Building a reporting pipeline
A complete nightly extract:
import os
import requests
API_KEY = os.environ['CODEDD_API_KEY']
BASE = 'https://api.codedd.ai/api/v1/enterprise'
session = requests.Session()
session.headers['Authorization'] = f'Bearer {API_KEY}'
def get(path, **params):
response = session.get(f'{BASE}{path}', params=params, timeout=60)
if response.status_code == 429:
raise RuntimeError(f"Rate limited; retry after {response.headers['Retry-After']}s")
if response.status_code == 409:
return None # Audit still running; nothing to extract yet.
response.raise_for_status()
return response.json()['data']
rows = []
for portco in get('/portcos/', limit=100)['portcos']:
group_audits = get(f"/portcos/{portco['portco_uuid']}/group-audits/", limit=100)
for group_audit in group_audits['group_audits']:
group_uuid = group_audit['group_audit_uuid']
debt = get(f'/group-audits/{group_uuid}/kpis/technical-debt/')
if debt is None:
continue
for audit in get(f'/group-audits/{group_uuid}/audits/', limit=100)['audits']:
# Skip repositories whose analysis has not produced results.
if not audit['is_completed']:
continue
scores = get(f"/audits/{audit['audit_uuid']}/scores/")
rows.append({
'portco': portco['name'],
'group_audit': group_audit['name'],
'repository': audit['name'],
'lines_of_code': audit['lines_of_code'],
'scores': scores['scores'],
'portfolio_technical_debt': debt['data'],
})
print(f'Extracted {len(rows)} repositories')
Recommended practice
- Store the key in a secret manager, never in source control, a notebook, or a dashboard definition.
- Rotate on a schedule — quarterly is a reasonable default — and always after someone with access leaves.
- Use one key per consuming system. When something misbehaves you can revoke it without taking down every other integration.
- Grant the narrowest scope the tool needs.
- Cache with
ETags and only re-read what changed. A completed audit's results never change. - Retry with exponential backoff on
429and5xx; never retry a4xxother than429. - Check
is_completedbefore extracting results, and treat409 audit_in_progressas "come back later", not as an error.
Security and auditability
- Keys are stored only as SHA-256 digests. Neither a database dump nor a query log yields a usable credential.
- Every request is recorded against the key — method, route, resource, status, and outcome — including refusals. Ask support for an access export if you need to evidence who read what.
- Access is re-checked on every request against your live contract, the key's scopes, and the resource's ownership. Nothing is inherited from when the key was created.
- Responses are marked
Cache-Control: private, no-storeso audit findings do not linger in an intermediary cache. - All traffic must be HTTPS.
Frequently asked
Can I write data through this API? No. Every endpoint is read-only. Audits are started in the app or through the CodeDD CLI.
Can I use it with an MCP client? The API is the foundation for MCP access to CodeDD data. Contact your account manager about current MCP availability.
What happens when our contract lapses? The API closes within a minute, and existing keys stop working. They resume if the contract is reinstated and has not expired.
Can a key read another firm's data?
No. A key is bound to one legal organization and can only reach portfolio companies, audits, and results that belong to it. Anything else returns 404.
Do invited team members access the API with their login?
No. The data endpoints do not accept session JWTs — only an organization API key (Authorization: Bearer codedd_ent_...). Invited members do not get automatic API access; an organization administrator must create a key and share it with the tool (or person) that needs it. Keys are org-scoped: whoever holds the key can read the firm's entire entitled audit estate, not just the subset one member sees in the web UI.
Who can create or revoke keys? Only administrators of the legal organization (the Enterprise billing entity), via Account → Enterprise plan → Data API keys, while on an active full Enterprise contract.

