Intangible Asset Valuation API: The Opagio Developer Guide

Brass balance scale on a wooden block in warm editorial light, representing the measured intangible asset valuation figures returned by the Opagio API

Most finance teams meet their valuation data through a screen. That works until the data needs to live somewhere else — a board pack assembled every quarter, an internal data warehouse, a lender reporting template, or an investor update that has to reconcile against the management accounts.

Opagio Public API v1 exists for that second case. It is a small, deliberately read-oriented REST surface that lets your systems pull the intangible asset valuations and company records already held in your Opagio organisation, without a person exporting anything by hand.

This guide documents what the API does today, verified against the shipped code rather than a roadmap. Where a capability does not exist, this guide says so.

★ Key Takeaway

Public API v1 is org-scoped, API-key authenticated, and read-oriented. It returns valuations and company records you already own, plus outbound webhook subscription management. It does not accept financial data for valuation, and it does not run calculations on demand.


What Public API v1 covers

Endpoint family What it returns Scope required
Valuations Your organisation's valuation reports, list and detail read:valuations
Portfolio KPI summary, sector breakdown, company records read:portfolio
Webhooks Outbound event subscriptions — create, list, update, delete read:webhooks / write:webhooks
Widget A four-figure summary for an embeddable widget Org match on the path

Two properties shape everything else. First, the API is org-scoped: every request is bound to exactly one organisation — the one that owns the key — and cross-tenant reads are not possible. Second, it is API-key authenticated, not session-cookie authenticated, so it is a separate path from the browser application and is not affected by anyone's login state.

Property Value
Base URL https://opag.io/api/v1
Auth Authorization: Bearer opagio_pk_…
Content type application/json
Versioning Path-based (/api/v1) plus an X-API-Version response header

Step 1 — Issue an API key

Keys are not created through the API. They are issued inside the platform, under Settings → API keys, by a user holding the API_MANAGE_KEYS permission in an organisation that has the api-access feature flag enabled. If the flag is off for your organisation, the key-issuance page will not mint a key — speak to your account contact before you plan an integration around it.

A newly created key is granted four scopes by default: read:valuations, read:portfolio, read:webhooks and write:webhooks.

⚠ Warning

The full key is displayed exactly once, at creation. Opagio stores only a SHA-256 hash of it plus an 18-character display prefix, so the value cannot be recovered or re-shown afterwards. If you lose it, revoke the key and issue a new one.

The same page lists your existing keys by prefix and revokes them. A revoked key, or one past its expiry date, is rejected immediately on the next call.


Step 2 — Authenticate a request

Every call carries the key as a bearer token:

Authorization: Bearer opagio_pk_<64 hex characters>

The prefix and the total length are validated before any database lookup. A key is opagio_pk_ followed by 64 hexadecimal characters — 74 characters in total. Anything else is rejected on format alone, which means a truncated or padded key fails fast rather than being treated as an unknown credential.

Authentication failures and what they mean

Condition Status Response body
Missing or non-Bearer header 401 API key required. Use Authorization: Bearer <key>
Wrong length or wrong prefix 401 Invalid API key format.
No matching key 401 Invalid API key.
Key revoked 401 API key has been revoked.
Key expired 401 API key has expired.

These are deliberately distinguishable. A client that retries on Invalid API key format. has a bug in its configuration handling; a client that sees API key has been revoked. should stop and alert a human rather than retry.


Step 3 — Understand the four scopes

Each key carries a permissions array. Every endpoint checks the one scope it needs and returns 403 with Insufficient permissions. if it is absent.

Scope-to-endpoint mapping

Scope Grants
read:valuations GET /valuations, GET /valuations/:id
read:portfolio GET /portfolio, GET /portfolio/companies, GET /portfolio/companies/:id
read:webhooks GET /webhooks
write:webhooks POST, PUT and DELETE /webhooks — and also satisfies GET /webhooks

The GET /widget/:orgId endpoint is the exception: it checks no named scope, and instead requires the key's organisation to equal the :orgId in the path. A mismatch returns 403.

ℹ Note

If you are building a read-only reporting job, there is a real security benefit in requesting a key without write:webhooks. The default grant is convenient, not minimal, and least privilege is worth the extra conversation.


Step 4 — Budget for two rate-limit ceilings

This is the detail most integrations get wrong, because there are two independent limits and a call must satisfy both.

300 requests per 15 minutes, per source IP
300 requests per 15 minutes, per API key
429 status returned when either ceiling is hit

The per-IP ceiling is applied before authentication, as a coarse anti-abuse guard. The per-key ceiling is applied after authentication, keyed on the authenticated key, and its budget is selected from the key's rate-limit tier. The standard tier is 300 requests per 15-minute window, and an unrecognised or absent tier falls back to the same default.

The two are additive constraints, not alternatives. Spreading one key's traffic across many source addresses does not buy extra throughput, because the per-key ceiling still applies. Equally, several keys behind one office IP share the per-IP budget between them.

The two limits return different messages, which is how you tell them apart in logs:

Ceiling hit Response body
Per-IP Rate limit exceeded. Please try again later.
Per-key API key rate limit exceeded. Please try again later.
✔ Example

A nightly job that pages through 2,000 portfolio companies at the maximum 100 records per page needs 20 calls. That sits far inside both ceilings. A per-company detail fetch afterwards — 2,000 more calls — does not, and should be batched across windows or replaced by the list endpoint's payload.


Step 5 — Read valuations and company records

Valuation reports are listed newest-first and scoped to your organisation. The internal userId field is stripped from every record before it leaves the API.

curl -s https://opag.io/api/v1/valuations \
  -H "Authorization: Bearer $KEY"

GET /valuations/:id returns a single report in the same shape. A report belonging to a different organisation returns 404, not 403 — the API reports another tenant's resource as absent rather than as forbidden, so existence itself is never leaked.

The read endpoints at a glance

Endpoint Returns Fields removed before response
GET /valuations Valuation reports, newest first, paginated userId
GET /valuations/:id One valuation report userId
GET /portfolio KPI summary plus sector breakdown
GET /portfolio/companies Company records, paginated, each with a derived health organizationId, createdBy
GET /portfolio/companies/:id One company record with health organizationId, createdBy
GET /widget/:orgId Four summary figures for an embeddable widget

The health classification on company records is derived from intangibles intensity rather than stored. For company-type organisations the records also carry a resolved sector, industryCategory and sicCode.

GET /widget/:orgId returns companyCount, totalEV, weightedAvgTFP and totalIntangibles. Where the organisation has no companies, it returns companyCount and totalEV as zero. The totalEV figure is enterprise value and weightedAvgTFP is weighted average total factor productivity.


Step 6 — Handle pagination and the response envelope

List endpoints accept page and per_page. Both are clamped server-side, so an out-of-range value is corrected rather than rejected.

Query parameter Default Bounds
page 1 1 or greater
per_page 25 1–100, clamped

List responses echo the values actually applied — { "data": [ … ], "page": 1, "per_page": 25 } — so a client should read the echoed values rather than assume its request was honoured verbatim. Pagination is offset-based.

Three response envelopes cover the whole surface. Reads and creates return { "data": … }. Updates and deletes return { "success": true }. Failures return { "error": "<message>" }.

Status codes you should handle

Status Meaning
200 Success
201 Webhook subscription created
400 Validation error in the body or parameters
401 Authentication failure
403 Missing scope, or widget organisation mismatch
404 Not found — also returned for another organisation's resource
429 Either rate-limit ceiling exceeded
500 Server error

Every v1 response carries an X-API-Version header of the form 1.0.<build marker>. It is set before authentication runs, so even a 401 carries it — useful when you are debugging whether a request reached the API at all.


Step 7 — Subscribe to events (briefly)

The API also manages outbound webhook subscriptions: POST /webhooks registers an HTTPS URL against a list of event names, and returns a signing secret once, in the creation response. Deliveries arrive as a POST carrying X-Opagio-Event and an X-Opagio-Signature header containing a hex HMAC-SHA256 of the raw body, keyed with that secret. Verify it against the exact raw bytes.

ℹ Note

Webhooks deserve their own treatment — the event catalogue, signature verification in practice, the ten-consecutive-failure auto-disable behaviour, and the absence of per-event retries. A dedicated companion guide covers them. Treat this section as orientation only.

One caveat worth stating now: registering an event name does not guarantee traffic. A number of names in the catalogue are declared for future use and have no emitter wired behind them yet. A subscription to one of those is accepted and will simply never fire.


What the API does not do

Being precise about absence is more useful than an aspirational feature list.

  • It does not accept financial data for valuation. There is no endpoint that takes inputs and returns a computed valuation. Valuations are produced in the platform; the API reads the results.
  • It does not write portfolio data. There are no create, update or delete endpoints for companies or valuations. The only writes on the surface are webhook subscriptions.
  • It does not expose benchmarking or questionnaire endpoints.
  • There is no published OpenAPI or Swagger schema, and no client SDKs. Any HTTP client will do; there is nothing to install.
★ Key Takeaway

Treat Public API v1 as a read replica of what you already have in Opagio, not as a valuation engine you can call. Integrations designed on that assumption tend to survive contact with the API; ones designed around an on-demand calculation endpoint do not.


A sensible first integration

A first integration that works well in practice is a scheduled pull rather than an event-driven one. Run a job on your reporting cadence, page through GET /portfolio/companies, store the response against your own record of the period, and keep the raw JSON alongside the parsed figures so that you retain an audit trail of exactly what the API returned on the day you pulled it.

That last point matters more than it sounds. Valuation figures move as underlying data is refreshed. A stored response with a timestamp is the difference between a board pack you can defend and one you can only re-run.

Once that job is stable, add a webhook subscription so the pull can be triggered by an event instead of a clock. Doing it in that order means you always have a working fallback if a delivery is missed.


Related reading

Share:

Ivan Gowan

Ivan Gowan — CEO, Co-Founder

25 years as tech entrepreneur, exited Angel

Connect on LinkedIn →

Try it yourself — Valuator

Estimate the value of your intangible assets using industry-standard methods like Relief from Royalty, MPEEM, and With & Without.

Open Valuator →

Related Articles

Antique brass balance scale on a wooden desk beside a leather-bound ledger in warm window light, one pan carrying a single calibration weight, representing the one verified delivery attempt Opagio makes for each outbound webhook event
outbound valuation webhooks 2026-09-03 · Ivan Gowan

Outbound Valuation Webhooks: The Opagio Developer Guide

A developer guide to Opagio outbound webhooks — how to register a subscription, verify the HMAC-SHA256 signature on every delivery, which events actually fire today versus which are declared for the future, and the failure behaviour you must design around, including the absence of retries.

Read more →

Subscribe to our newsletter

Get the latest insights on intangible asset growth and productivity delivered to your inbox.

Want to learn more about your intangible assets?

Take the free intangible asset assessment to see where your business stands across Opagio 12.