Skip to content
CodeDD Logo

Enterprise Data API

Read your CodeDD audit and portfolio data from your own BI tools, warehouses, and reporting pipelines

Portfolio Analytics33 min readUpdated View as Markdown
On this page

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.

Quick start#

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

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

You never have to construct an identifier. Start from the key and walk down:

Text
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 (versioned, see Schema versions)
/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. Additive fields: the repository's tier as CodeDD shows it (archetype_tier_effective), its certainty (archetype_tier_certainty: exact or at_least, a lower bound) and method (archetype_tier_basis: requirement_ladder or legacy_centroid), and cohort_tier_basis (legacy_centroid: peers are matched on the previous method's tier, archetype_primary_tier)
/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". The architecture resource is the exception to "payload as the dashboard sees it": it has a stable, versioned shape of its own (see Schema versions).

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 (versioned)
/api/v1/enterprise/group-audits/{group_audit_uuid}/executive-summary/Portfolio-level narrative (versioned; v2 adds its provenance)
/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
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.

The three versioned resources also state the schema version of data in meta.schema_version.

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:

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

The architecture resource can change after an audit completes: when an admin curates the repository's architecture style, CodeDD stores the repository's architecture model later, or its requirement ladder is recomputed. Its ETag changes with it, so a conditional request then returns the new representation instead of a 304. The tier-benchmark resource's ETag also follows the requirement ladder and the method the tier is shown by.

Schema versions of the architecture resources#

CodeDD's architecture analysis keeps improving. So that an improvement never changes the data your pipeline parses without warning, the three architecture resources have a schema version:

ResourceEndpoint
Repository architecture/api/v1/enterprise/audits/{audit_uuid}/architecture/
Estate map/api/v1/enterprise/group-audits/{group_audit_uuid}/estate-map/
Portfolio executive summary/api/v1/enterprise/group-audits/{group_audit_uuid}/executive-summary/
VersionWhat it is
1 (v1-legacy)The shape these resources had before versions existed, frozen: no field is renamed or removed, and fields CodeDD adds appear only in v2.
2A typed shape with explicit fields: what every relationship means and what evidence it rests on, coverage and data quality, one architecture style with its source, the tier with its status, and no file-system paths. v2 only grows: new fields may be added, existing ones keep their name and meaning.

All other endpoints are unversioned and ignore the selector.

Selecting a version#

Shell
# Query parameter
curl -H "Authorization: Bearer $CODEDD_API_KEY" \
  "https://api.codedd.ai/api/v1/enterprise/audits/$AUDIT/architecture/?version=2"

# Or a header
curl -H "Authorization: Bearer $CODEDD_API_KEY" -H "X-CodeDD-Schema-Version: 2" \
  https://api.codedd.ai/api/v1/enterprise/audits/$AUDIT/architecture/
  • Accepted values are 1 and 2 (v1, v1-legacy and v2 work too). Any other value is a 400 invalid_parameter, as is a query parameter and a header that name different versions.
  • The response states the version it carries: in meta.schema_version and in the X-CodeDD-Schema-Version response header (responses vary on that header). A v1 response served because the request named no version also carries a Sunset header with the date below: it marks the end of v1 as the default. A request that names version=1 gets no Sunset header, because explicit v1 keeps working after that date.
  • Each version has its own ETag, so a conditional request never answers with a 304 for the other shape. The ETag also changes when the version's output changes for the same audit (v2 gains a field, a value rule is corrected), so a cached copy is never revalidated across such a change. The ETags of the architecture resource changed once when versions were introduced; expect one full re-read.

Default version and sunset plan#

PeriodRequest without a versionversion=1version=2
Until 30 April 2027v1-legacyv1-legacyv2
From 1 May 2027v2v1-legacy, until its removal is announced in this documentv2

Send version=2 now: requests that name a version are unaffected by the default changing.

What can change in each version#

VersionFieldsValuesRetirement
v1-legacyFrozen. No field is added, renamed or removed.A value the analysis now computes correctly is served corrected, under the same field. Every correction is listed in the v1-legacy tables below.Stops being the default on 1 May 2027, as shown above. Explicit version=1 keeps working until a removal date is announced in this document. Removal is never unannounced.
v2Grows only. New fields are added and documented in the v2 section. Existing fields keep their name, type and meaning.Corrected like v1. A field that is null today may carry a value once the analysis records it.No sunset is planned. A change that would rename, remove or redefine a field gets a new schema version and is never made in v2.

Both versions change their ETag whenever their output for the same audit changes, whether a field is added or a value rule is corrected. A conditional request therefore never keeps a stale copy. Parse v2 leniently: ignore fields you do not know.

v1-legacy#

v1-legacy keeps the fields and nesting these resources had before versions existed. Fields the analysis has gained since appear only in v2. These include coverage and data quality, edge semantics and relationships, structured evidence and candidate links, detection coverage, the shared stack, dimension evidence states, repository kinds, deployment platforms, the estate stages and membership, the org consolidation, structural criticality, module dependencies, provenance and the requirement ladder. The frozen field set reaches into the documents CodeDD's classifiers store: dimension scores, confidence components, tier gap and ceiling analyses, the repository tier breakdown and the group topology summary keep the fields they had.

Only documents without a fixed schema are served as stored, as before, and may carry whatever the analysis wrote: a component's or relationship's properties (the LLM's component document), the insights_* blocks, and the detector blocks coupling_analysis, communication_patterns, flow_patterns, coupling_metrics and communication_flows. Treat their keys as data, not as part of the contract.

The values are not frozen: where the analysis now computes a value correctly, v1 carries the corrected value under the same field.

Repository architecture

FieldCorrection
archetype_classification.archetype_name, tier_reference.*.name, tier_gap_analysis.next_tier_nameScale tier names (Foundational, Established, Scalable, High-Scale, Hyperscale). A tier describes operational scale, not an architecture style.
archetype_classification.architecture_style (and _label), executive_overview.service_architectureThe repository's architecture style decided from evidence, unknown when undecided, instead of a guess from the number of components.
archetype_classification.primary_tier, archetype_name, confidence, tier_gap_analysisprimary_tier is null when the scale-tier ladder does not apply to the repository (a library, infrastructure code, a command-line tool, ...) or too few of its dimensions have evidence; archetype_name then reads Not applicable or Insufficient evidence and tier_gap_analysis names no next tier. A dimension without evidence no longer counts as the lowest tier, and confidence is how decisively the evidence places the tier, scaled by the share of dimensions with evidence and the completeness of the analysed inputs.
archetype_classification.dimension_scores.deployment_model, tier_gap_analysis.actions.*.recommendationThe deployment model scores deployment independence and automation (build artefacts, deployment declared as code, automated deploys, environment promotion, zero-downtime and progressive rollouts), not the number of deployable units; platform-managed hosting (PaaS, serverless, ECS) is not scored below Kubernetes. Tier recommendations are fitted to the repository's architecture style, platform and stack and never recommend a capability it already has: no decomposition advice for a single deployable, no Kubernetes advice for a repository on ECS or a PaaS.
executive_overview.key_metrics.coupling_levelnull when the component graph cannot support a coupling figure (edge extraction missing or failed, or fewer than half of the source packages represented).
executive_overview.key_metrics.primary_communicationDerived from the graph's synchronous and asynchronous edges.
executive_overview.critical_dependencies, insights_critical_dependencies, key_metrics.critical_dependencies_countSingle points of failure of the component graph (a component that at least 3, and at least 15% of, the graph's components depend on at runtime) instead of keyword matches. The count is null when the graph cannot be searched.
confidence_basis on edgescorroborated means the source component's files literally reference the target; edges only consistent with other LLM output are mentioned.
dependency_flow.critical_paths.critical_components, critical_paths.critical_relationships, visualization_data.enhanced_edges[].is_criticalStructural criticality of the component graph, replacing the LLM's high criticality and strength labels. A component is critical when it is an articulation point (removing it cuts at least two components off the rest), has a fan-in at the graph's 90th percentile and at least 3, or is a single point of failure. An edge is critical when it is the only link between part of the graph and the rest (a bridge). Both lists are empty when the graph cannot be assessed (no graph, no relationships).
visualization_data.enhanced_edges[].visual_properties.stroke_styleFollows the edge's evidence: solid for static evidence (an import, a literal reference, a matched pattern), dashed for an LLM claim consistent with other analysis, dotted for an LLM inference or an edge whose evidence was not recorded.
metadata.analysis_timestampISO 8601 (UTC) instead of always null.
metadata.file_analyses_summary.total_filesCounts the stored file analyses (it read 0).
Every text valuePaths of CodeDD's analysis machines are removed; file references are repository-relative.

Estate map

FieldCorrection
estate_summary.total_cross_repo_edges, estate_summary.coupling_densityCount directed dependency edges between estate repositories only. Shared technology and delivery links (CI, deployment, base images) are not coupling. Edges are matched to repositories by audit, not by name, so the edges of two repositories that share a name are counted.
edgesNew group audits no longer record shared technology (two repositories using PostgreSQL) as an edge; v2 lists it as shared_stack. Directed edges read "source depends on target" (event_dependency edges are no longer served publisher first), undirected ones list their repositories in name order, an edge served in reverse carries no is_critical_path, and evidence that differs only in case or a technology alias is listed once. strength follows confidence (high from 0.85, medium from 0.6); an edge several independent detectors found combines their confidences (detectors that restate the same repository analysis, such as a declared integration and the producer/consumer pairing built from it, count once). A relation curated under an older type name (shared_datastore, shared_library, event_bus) is served under its current type (data_dependency, library_dependency, event_coupling). New group audits no longer draw an API edge between two repositories only because they share a business domain.
estate_summary.choke_points, estate_summary.blast_radius, org_archetype.topology_summaryOnly repositories that others depend on through directed edges, with at least medium evidence confidence. A dependency several detectors found counts once in fan-in, choke points and roles; a shared data store adds no fan-in to either repository. A blast radius lists every repository a failure reaches, directly or through other repositories; a delivery link (base image, CI templates, deployment assembly) reaches only the repositories that build or ship with it, not their callers. A choke point is matched to its repository by audit, so a repository that shares its name with another is not dropped.
archetype_name, estate_summary.org_archetype_name, org_archetype.org_archetype_name, tier_ceiling_analysis.next_tier_nameScale tier names.
archetype_primary_tier, estate_summary.tier_composition, estate_summary.org_primary_tier, org_archetype.org_primary_tier, org_archetype.tier_gap_analysis, org_archetype.tier_ceiling_analysisA repository without a tier (see the repository architecture) has a null tier and is counted under not_applicable or insufficient_evidence in tier_composition. When no repository of the estate has a tier, the estate has none either: a null org tier and no tier gap or ceiling.
architecture_style, architectural_style, estate_summary.style_compositionThe architecture style decided from evidence, as on the repository architecture.
domains[].recommendationsDrawn from evidence: a critical-path repository of the domain, a score below the estate average (no longer described as a tier-peer comparison), and the largest next-tier gaps of the domain's repositories with recommendations fitted to each repository; no tier templates. v2 adds the trigger each one cites.
edges (integration edges)New group audits record an api_dependency or event_coupling edge when a repository's runtime configuration or code references something another estate repository publishes, such as a configured endpoint URL or host, a service name, or a topic or queue. The edge's evidence names the reference, the file it was read from and the identity it matched. A match too weak to draw is not an edge. v2 lists it under relationship_candidates with its reason.
edges, estate_summary, org_archetype.topology_summary (relationships)One coupling is counted once. When several detectors or edge types state the same dependency between two repositories over one channel, that dependency counts once in fan-in, choke points, criticality and roles. edges still lists one entry per edge type. v2 groups them as relationships.
domains, estate_summary.total_systemsList and count only the business domains that at least one repository of this estate map belongs to, plus Unclassified. An organization domain without a repository in this group audit is not a system of this estate. v2 lists every domain of the organization as org_domains.
is_user_curated_domain, is_user_curated_role, is_user_curated_style, is_user_curated_topology and the curated valuesA curation is kept for the repository itself, identified by its organization and git remote, so a re-audited repository keeps its curated domain, role, style, topology role and relations.
estate_summary.org_primary_tier, org_archetype.org_primary_tier, org_confidence, org_dimension_scoresOne consolidation, shared by the group audit, curation and the KPIs. Each repository is weighted by the square root of its lines of code, and in an estate of three or more repositories none carries more than 45% of the weight. Topology roles change the weights only when at least half of the repositories have dependency evidence. A repository a user excluded from the estate tier ("Exclude from benchmark") or one without a tier takes no part. v2 lists those under org_tier.excluded_repos.
domains[].aggregate_scoresThe domain's code health. A repository without audit scores no longer counts as seven zero scores, and a category scored 0 is no longer dropped from composite. composite is the mean of the categories at least one scored repository reports, and each category is the lines-of-code-weighted mean over the scored repositories. The object keeps its fields. v2 adds scored_repo_count.

Portfolio executive summary: summaries generated with evidence guardrails (v2 provenance.guardrails_applied) make no coupling, role or tier claim the evidence does not support, and state when cross-repository coupling cannot be determined.

v2#

Conventions for every v2 resource:

  • Every documented field is always present; null means unknown, never zero: a count is 0 only when CodeDD counted nothing, and a ratio without a denominator (dependency_edges_per_repo of an estate without repositories) is null. Flags such as is_external and the curated flags are false unless set.
  • Every confidence is an integer from 0 to 100.
  • File references are repository-relative paths; no field contains an absolute path.
  • Stored LLM documents (component properties, insight blocks, detector blobs) are not served; their facts are, as typed fields.
  • A list taken from an unbounded source is capped and its total is served beside it (for example external_systems.total).

Vocabularies

FieldValues
relation_kinddirected (source depends on, or acts on, target), undirected (no direction; for example two repositories sharing one data store)
relation_classdependency, delivery (build, CI, deployment, base images), commonality (shared technology, never coupling), structural (one component contains another), unclassified
evidence_tierdeterministic (static evidence in files: imports, manifests, configuration, literal references), curated (asserted by a user of your organization), heuristic (rule-based pairing without a cited file), llm (an LLM's claim), unknown (not recorded)
tier.tier_statusapplicable, not_applicable (the scale-tier ladder does not apply to this kind of repository, e.g. a library or infrastructure code), insufficient_evidence; null when no classification exists
requirement_ladder_summary.basisrequirement_ladder (the tier is shown by the requirement ladder), legacy_centroid (the tier is shown by the previous method; available is false when no ladder was computed)
requirement_ladder_summary.certaintyexact, at_least (a lower bound: the next tier could not be verified; never compare it as an exact tier)
requirement_ladder_summary.areas[].next_stateblocked (the next tier was checked and is not met), not_verified (could not be assessed), beyond_checks (no check exists yet for the next tier), at_top
repo_kind.sourceclassifier (classified and stored by the analysis), derived (classified when served, for audits stored before kinds were), none (not determined), display_labels (older audits: a label-only kind)
coverage.data_sourceserver (the analysis read a server-side clone), cli_v1 (a CLI submitted its own computed document), cli_v2 (a CLI run driven by the server); null when not recorded
deployment_platformcontainer_orchestrated, container_simple (containers on hosts), paas, serverless, vm, unknown (no deployment target found); null when no deployment profile was recorded

Estate edge types. Every relationship between repositories has one of the types below. A type's meaning does not change. A type can gain an alias, and an edge stored under an alias is served under the type.

typeMeaningrelation_classrelation_kindchannel
vcs_dependencyPulls source code from the target (submodule, git dependency)dependencydirectedlibrary
library_dependency (alias shared_library)Depends on a library the target publishesdependencydirectedlibrary
build_inheritanceBuilds against build configuration the target publishes (parent POM, BOM, composite build)dependencydirectedbuild
catalog_relationDeclares a dependency on the target in its service catalogdependencydirectedcatalog
contract_dependencyConsumes API contracts or schemas the target ownsdependencydirectedcontract
interface_dependencyCalls an interface of the target named in the architecture analysisdependencydirectedhttp
api_dependencyCalls the target's API (configured endpoint, service name, host)dependencydirectedhttp
api_consumer_producerAPI call paired from consumer and producer rolesdependencydirectedhttp
event_coupling (alias event_bus)Consumes a topic or queue the target publishesdependencydirectedevent
event_dependencyConsumes from the messaging infrastructure the target publishes to; no shared topic was matcheddependencydirectedevent
data_dependency (alias shared_datastore)Both reference the same data storedependencyundirecteddata
uses_base_imageBuilds on the target's base imagedeliverydirectedbuild
ci_orchestrationRuns CI/CD workflows or pipeline templates maintained in the targetdeliverydirectedbuild
deploy_assemblyAssembled and deployed by the target (a platform or GitOps repository)deliverydirecteddeploy
shared_technology, shared_messaging, shared_databaseBoth use the same technology, messaging infrastructure or data store name; no shared instance is impliedcommonalityundirectedcommonality
unknownA link whose kind was not recordedunclassifiedundirectedunclassified

Repository architecture (v2)

FieldContents
schema_version"2"
analysis_timestamp, summaryWhen the analysis ran; its narrative summary
repo_kindkind, label, modifiers (e.g. monorepo), confidence, source, reasons
stylevalue, label, source (user, deterministic, llm, none), basis, confidence, conflict, conflicting_values, unverified_value (a suggestion while the style is unknown), candidates with their evidence
tieravailable, tier_status, tier_status_reason, primary_tier, name, confidence, mixed_tier_note, dimensions
metricsComponent, relationship and technology counts, coupling_edges_per_component with coupling_measurable, primary_communication, single_points_of_failure_count with single_points_of_failure_complete
technologies, patternsDetected technologies with confidence and evidence; architectural patterns
componentsGraph nodes (first-party and is_external systems): domain, criticality (the LLM's label), confidence, evidence_tier, evidence_basis, key_files (at most 25) and key_file_count, and structural_criticality (below)
edgessource, target, type, relation_kind, relation_class, communication_type, confidence, evidence_tier, evidence_basis, evidence (at most 10) and evidence_count, and is_critical with its criticality_reason (a critical bridge)
single_points_of_failureComponents many others depend on at runtime
coveragereported, run_status, file and package counts, inventory_capped, external_systems_detected, edge_evidence_tiers (edges per evidence tier), data_source
data_qualitylevel (good, partial, low, unknown) and the reasons behind it
validationThe structural validation of the analysis: status and its checks
architecture_runHow the analysis run ended: status (completed, partial, failed, unknown), its reasons, and the basis it is known from (run_record, coverage for audits analysed before runs were recorded, none). An unrecorded run is unknown, never a success.
structural_criticalityWhat holds the component graph together: assessed (with not_assessed_reason when the graph cannot be searched), complete (false when the graph is partial and the result a lower bound), graph_limitations, node and link counts, deterministic_link_count, critical_node_ids and critical_link_count. available is false for audits analysed before it existed.
module_dependenciesThe deterministic module-level import graph: status (available, single_module, unsupported when no resolver exists for the main language, outdated_resolver, not_analysed, unavailable), primary_ecosystem, resolution_rate, ecosystems, structure (cycles, instability, layer violations), modules_total and edges_total, and the 25 most connected modules and 40 heaviest edges
modelWhere the facts above come from: the typed architecture model of the audit. available, basis (stored by the analysis, or legacy_adapter: rebuilt from the stored results of an audit analysed before the model existed), source, schema_version, algorithm_version with current_algorithm_version and algorithm_current, model_hash (changes only when a fact changes), stored_at and run_status. available is false, with every other field null, when no model could be built.
requirement_ladder_summaryThe repository's tier by the requirement ladder (see "How the tier is determined" below): available, basis, tier_status, tier, certainty, next_tier, catalogue_version (the requirement list, e.g. ladder-v0.2), rule_id (ladder_staged_v2), areas (area, applies, level, next_state per area) and lift (build_count, confirm_count, not_checked_count for the next tier). No requirement results, evidence or file paths. available is false, with every other field null, for an audit analysed before the ladder.

A component's structural_criticality is {is_critical, reason, reasons, fan_in, fan_out}. Each entry of reasons is a code with its figures: articulation_point (separated_nodes, parts), high_fan_in (fan_in, percentile) or single_point_of_failure (dependent_count). It is null, and so is an edge's is_critical, when the graph could not be assessed or the audit predates structural criticality. Before that, is_critical was the LLM's strength label, and v2 never serves it as structural criticality.

A component of the LLM synthesis has the evidence_tier llm: its boundary, name and responsibilities are the model's, even when the files it cites are real. Its evidence_basis says what backs it: repository_files_verified (the cited files were checked against the analysed repository), repository_files (cited, not checked) or analysis_only (no file cited). A component of the deterministic fallback graph (deterministic_fallback) is deterministic, and an external system (relationship_target) takes the strongest tier of the edges that reference it.

Estate map (v2)

FieldContents
summaryRepository, line and system counts; dependency_edge_count (directed dependencies: the only coupling), dependency_undirected_count, commonality_count, delivery_edge_count, dependency_edges_per_repo, critical dependency counts, role, tier and style composition, choke_points, governance_coverage
org_tierThe estate's tier: available, tier_status, primary_tier, name, confidence, dimensions, and the consolidation behind it: excluded_repos (each with its reason: user_excluded, not_applicable, insufficient_evidence or classification_unavailable), loc_concentration (the repository holding most of the code, with the capped weight it got) and consolidation (model, input_hash). The three are null for estates consolidated before they were recorded.
reposOne entry per repository: repo_kind, style, tier, estate and topology role, curated flags, governance, scores, evidence_status, dependency_assessed, external_systems (the 25 most confident with total and truncated), deployment_platform with its label, estate_membership (member, in_topology, reason) and requirement_ladder_summary (the repository's ladder tier in the estate, as on the repository resource)
domainsBusiness domains with a repository in this estate map: repo_audit_uuids, aggregate_scores (code health; every value null when no repository has audit scores), scored_repo_count, and recommendations (each with the trigger it was drawn from)
org_domainsEvery business domain of your organization, including those with no repository in this estate map (repo_count 0)
edgesTyped edges between repositories, one per edge type and orientation: relation_kind, direction_meaning, relation_class, channel, evidence_tier, evidence_basis, confidence, evidence_confidence, coupling_weight, detector, is_critical_path, the subject they name (technology, library, topic, service, ...), their evidence and structured evidence_items, and for integration edges the identity_tier, matched_identity and integration_signals. relationship_id names the relationship the edge belongs to, and facet_id the edge itself.
relationshipsOne per source, target and channel: the unit that fan-in, choke points and criticality count. It has a stable edge_id, its typed edges as facets (one per type and detector, each with its own confidence and evidence), evidence_confidence, coupling_weight, the strongest evidence_tier, and the merged evidence_items
relationship_candidatesPairings too weak to draw, proposed for curation, each with its reason: below_confidence_threshold, loose_identity_match, broker_only (both use one broker, no topic compared), domain_http_pair or domain_single_producer (paired only by shared business domain). They are never relationships and never count as coupling.
shared_stackTechnology two or more repositories use, with how each repository was found to use it. Commonality, never coupling.
detection_coverageWhat the cross-repository detection could see, per repository and for the estate, including estate.coupling_verdict: no coupling verdict is drawn from missing evidence
estate_analysisThe estate stages (business_domains, estate_topology, ic_summary): the overall state (done, running, pending, failed, not_recorded), each stage's status, error, attempts and timing, and stale when membership changed after a stage ran
estate_membershipWhich sub-audits take part: included_count (members whose audit completed), topology_count, shown_count, and the excluded sub-audits with their reason. available is false when the membership could not be read.
requirement_ladder_summaryThe company tier by the requirement ladder (rule_id weighted_quantile_v1): the fields of the repository summary, plus input_snapshot_hash (the same hash means the same inputs) and computed_at. org_tier keeps the previous method's values.

How the tier is determined (requirement ladder). A tier is the highest level whose required capabilities are all evidenced, cumulatively, in every area that applies to the repository. Each requirement is Met, Not met, Not assessed (it could not be checked, for example because it is configured outside the repository, or CodeDD does not check it yet) or Not applicable, and Not assessed never counts against a tier. When the next tier could not be verified, the tier is a lower bound (certainty at_least). The company tier is reached in an area when repositories holding at least 80% of the weight (the square root of their lines of code) reach it there, plus every repository rated critical; the company tier is the lowest area. Libraries, infrastructure code and other repositories without a running scale are not rated. basis says which method the tier is shown by; the tier and org_tier blocks keep the previous method's values until every consumer switches, and v1-legacy does not change.

Two confidences. evidence_confidence (0 to 100) answers whether the relation is real. Detectors that read the same input count once, and independent ones are combined (noisy-OR), so a relation found by the runtime configuration and by the architecture analysis scores above either finding. coupling_weight (0 to 1) answers how significant the relation is architecturally. It is the fixed weight of the edge's type in the edge-type registry, and a relationship takes the largest weight of its types. The weights are: deploy_assembly 1, catalog_relation 0.95, build_inheritance 0.9, interface_dependency 0.88, vcs_dependency and contract_dependency 0.85, ci_orchestration 0.8, api_dependency 0.75, library_dependency, event_coupling and event_dependency 0.7, data_dependency and uses_base_image 0.65, api_consumer_producer (paired from consumer and producer roles) 0.6, unknown 0.5, and shared_technology, shared_messaging and shared_database 0. One is never folded into the other. confidence on an edge equals its evidence_confidence.

Structured evidence. Each item of evidence_items is {repo, file, signal, detector, identity_tier}: the repository whose file holds the evidence, the repository-relative file, the signal that matched (for example BILLING_URL=http://billing-api:8080), the detector that read it, and how precisely the target was identified (coordinate, git, image, chart, runtime_name, host, alias, repo_name, ...). A list holds at most 20 items, and evidence_item_count gives the total.

Portfolio executive summary (v2)

JSON
{
  "schema_version": "2",
  "group_audit_uuid": "8c1e...",
  "executive_summary": "## 1. ...",
  "provenance": {
    "generated_at": "2026-10-04T12:30:00+00:00",
    "input_hash": "3f7a...",
    "guardrails_applied": true,
    "coverage_caveat_applied": true
  }
}

input_hash is the SHA-256 of the input the summary was generated from: a changed hash means a summary generated from different data. coverage_caveat_applied says whether the summary states that cross-repository coupling cannot be determined from the evidence. Both are null (and guardrails_applied is false) for summaries generated before provenance was recorded. When the provenance cannot be read, every provenance field is null, guardrails_applied included: unknown, not false.

Example: one estate edge in v2 (abridged: the audit and domain identifiers of both ends and the empty subject fields are left out)

JSON
{
  "source_repo": "checkout",
  "target_repo": "payments",
  "type": "api_dependency",
  "relation_kind": "directed",
  "direction_meaning": "depends_on",
  "relation_class": "dependency",
  "evidence_tier": "deterministic",
  "evidence_basis": "static_evidence",
  "confidence": 85,
  "is_critical_path": true,
  "criticality_kind": "structural",
  "subject": { "service_name": "payments-api" },
  "evidence": ["config/services.yml: PAYMENTS_URL"],
  "evidence_count": 1
}

Example: the relationship that edge belongs to (abridged). A second detector found the same call in the architecture analysis. Its finding is a separate facet, and because it is independent evidence it raises evidence_confidence.

JSON
{
  "edge_id": "rel_3c9d0e5a71b24f6a8d10",
  "source_repo": "checkout",
  "target_repo": "payments",
  "channel": "http",
  "relation_kind": "directed",
  "relation_class": "dependency",
  "is_coupling": true,
  "types": ["api_dependency", "interface_dependency"],
  "evidence_confidence": 93,
  "coupling_weight": 0.88,
  "evidence_tier": "deterministic",
  "is_critical_path": true,
  "facets": [
    { "type": "api_dependency", "detector": "integration_matcher", "confidence": 85, "evidence_tier": "deterministic" },
    { "type": "interface_dependency", "detector": "architecture_bridge", "confidence": 55, "evidence_tier": "heuristic" }
  ],
  "facet_count": 2,
  "evidence_items": [
    { "repo": "checkout", "file": "config/services.yml", "signal": "PAYMENTS_URL=http://payments-api:8080", "detector": "integration_matcher", "identity_tier": "runtime_name" }
  ],
  "evidence_item_count": 1
}

Moving from v1 to v2#

v1-legacyv2
archetype_classification.primary_tier, archetype_name, confidencetier.primary_tier, tier.name, tier.confidence (check tier.tier_status first)
archetype_classification.architecture_style, executive_overview.service_architecturestyle.value (with style.source and style.conflict)
visualization_data.enhanced_nodescomponents
visualization_data.enhanced_edges, component_view.relationshipsedges
executive_overview.key_metricsmetrics
executive_overview.critical_dependenciessingle_points_of_failure
metadata.coverage, metadata.data_completenesscoverage, data_quality
dependency_flow.critical_paths, visualization_data.enhanced_edges[].is_criticalstructural_criticality, components[].structural_criticality, edges[].is_critical
estate_summary.total_cross_repo_edges, coupling_densitysummary.dependency_edge_count, summary.dependency_edges_per_repo (read detection_coverage.estate.coupling_verdict before drawing a coupling conclusion)
edgesedges (typed) and relationships (one per source, target and channel, the unit criticality counts)
domains[].repos[]repos (and domains[].repo_audit_uuids)
domains[].aggregate_scoresdomains[].aggregate_scores with scored_repo_count
org_archetypeorg_tier (with excluded_repos, loc_concentration and consolidation)

The OpenAPI document of the API describes both versions of each resource (oneOf).

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 (including an unsupported schema version). 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.
JSON
{
  "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:

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')
  • 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. Of a completed audit, only the architecture changes later (through curation or a stored architecture model), and its ETag changes with it.
  • 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.

Suggested

  • OverviewGetting Started
  • Audit Process OverviewSoftware Audit
  • CLI InstallationCodeDD CLI
  • Enterprise Data APIPortfolio Analytics
  • AI Agent Telemetry (OpenTelemetry Setup)Portfolio Analytics
  • Data Encryption at RestSecurity & Privacy

See these numbers on your own codebase

Run a first audit free, or walk through a live report with the CodeDD team.