---
title: Enterprise Data API
description: Read your CodeDD audit and portfolio data from your own BI tools, warehouses, and reporting pipelines
category: Portfolio Analytics
order: 2
---

# 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

```bash
# 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.

| 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:

```json
{
  "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`:

```python
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:

```bash
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`. |

```json
{
  "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:

```python
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 `ETag`s** 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](/documentation/codedd-cli-guide).

**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.

## Related documentation

- [Portfolio Monitoring](/documentation/portfolio-monitoring)
- [Setting Up a Portfolio Organization](/documentation/setting-up-a-portfolio-organization)
- [CodeDD CLI Guide](/documentation/codedd-cli-guide)
