DocumentationPortfolio AnalyticsEnterprise Data API

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.

  1. Click Create API key.
  2. Give it the name of the tool that will use it — Power BI production, not test — so you know what breaks if you revoke it.
  3. Choose the scopes it needs (see below).
  4. Choose a lifetime. Every key expires; the maximum is 365 days.
  5. 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.

ScopeGrants
read:auditsDiscovery (PortCos, group audits, repository list), PortCo settings, and all single-audit data endpoints
read:portfolioAll 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).

EndpointReturns
/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.

EndpointReturns
/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.

EndpointReturns
/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 areaAvailable via APINot exposed (by design)
PortCo navigation/portcos/, settings, KPI historyMember management, DORA OAuth config, remediation toggles
Group audit discoveryMetadata, member auditsPipeline progress, invitation workflow, scope selection
Overview / BenchmarkKPIs, benchmark, executive summary, category breakdown, financialsComparison matrix UI config
ArchitectureEstate mapEstate curation writes
Development ActivityDevelopment overview, repo activity, DORA, integration assessmentInnovation commits CSV export
Debt & SecurityTechnical debt, security findings, supply chainSaved Issue Compass selections, finding detail drawer
AI-NativeAI-Native assessment, per-repo AI authorshipAI delivery funnel
Single audit — SummaryScores, summaries, executive, files (aggregates)Per-file tree, file search
Single audit — FilesStatistics, extension mix, test coverage, complexityFile treetable, file detail drawer
RemediationRemediation dashboard (module-gated)
Admin / opsAudit 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.

HTTPerror_codeMeaning and what to do
400invalid_parameterA parameter failed validation. Fix the request.
401invalid_api_keyMissing, malformed, or unknown key. Check the header.
401api_key_expiredPast its expiry. Rotate it.
401api_key_revokedRevoked by an administrator. Ask for a new one.
403insufficient_scopeThe key lacks the scope this endpoint needs. Mint a key with it.
403enterprise_plan_requiredNo active Enterprise plan on the organization.
404resource_not_foundThe resource does not exist or is not covered by your key. These are deliberately indistinguishable.
409audit_in_progressThe audit has not finished, so this result set does not exist yet. Retry later.
429rate_limit_exceededHonour the Retry-After header.
500internal_errorOur 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

BucketLimit
Standard endpoints, per key120 requests/minute
Heavy endpoints (findings, dependencies, files, all portfolio aggregates), per key30 requests/minute
All endpoints, per organization600 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 429 and 5xx; never retry a 4xx other than 429.
  • Check is_completed before extracting results, and treat 409 audit_in_progress as "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-store so 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.