Migrate from v1 and v2
v1 and v2 are deprecated. They keep working unchanged until 28 April 2027, and then stop. Every response from them, errors included, carries these headers:
Deprecation: @1793145600
Sunset: Wed, 28 Apr 2027 00:00:00 GMTDeprecation is the announcement, 28 October 2026, as a Unix timestamp; Sunset is when the version stops working.
What replaces what
| v1 and v2 | v3 |
|---|---|
GET /company/{companyId}/emissions/summary | Same path on /v3, unchanged |
GET /company/{companyId}/portfolios | Same path on /v3, unchanged |
GET /company/{companyId}/portfolios/{portfolioId}/emissions/summary | Same path on /v3, unchanged |
GET /company | GET /v3/company, reshaped |
POST /company/{companyId}/data/data-point (both bodies) | POST /v3/company/{companyId}/data-points, reshaped |
POST /company/{companyId}/finance/posting-journal-line | POST /v3/company/{companyId}/data-points, mapped |
GET /partner/{partnerCode}/company/{erpCompanyId}/benchmark/real-estate | None: it stops working at the sunset |
For the emissions and portfolio operations, change /v1 or /v2 to /v3 and you’re done.
API keys
Your existing API keys work on v3. What each key may do is shown in the Scopes column of the API keys page API keys page . Keys created before scopes were introduced show company:read, data-point:write and benchmark:read: they can list companies and upload data, but not read emissions or portfolios. For those, create a new key with emissions:read or portfolio:read.
The older sign-in flow, which exchanged a secret for a temporary token at POST /v1/auth/generate-token, and GET /v1/emissions/organization/… have been retired. Use an API key as described in Authentication.
Errors
Every error from v3 is in the Problem Details format, with a stable code and per-field errors: see Errors and limits. v1 and v2 keep their current error bodies until the sunset.
A revoked API key is a 401 on v3, where v1 and v2 answer 400.
Company list
GET /v3/company returns:
{
"data": [{ "id": "1042", "name": "Energi.AI AS", "orgNumber": "927611074" }],
"meta": { "nextCursor": null }
}- The companies are in
data, ordered byid, instead oflist. totalis gone: countdatainstead.orgNumberisnullwhen unknown.- A query parameter is a
400: the list takes none.
Data-point upload
POST /v3/company/{companyId}/data-points takes { "data": [...] } with 1 to 500 records, and stores all of them or none: see Upload data. Compared with the v2 body:
| v2 | v3 |
|---|---|
cost + currency | cost: { "amount": …, "currency": … }, with an ISO 4217 currency |
usageAmount | usage (usageUnit stays) |
vendorOrgNum, customerOrgNum | vendorOrgNumber, customerOrgNumber |
vendorCountryCode, customerCountryCode | Same names, but must be ISO 3166-1 alpha-2 |
date, dateTo | A date (YYYY-MM-DD) or a UTC timestamp ending in Z. dateTo is inclusive and may not be before date; with dateTo, any time of day is dropped |
| Up to 1,000 records | Up to 500 records |
dimensions | At most 10 per record |
Also:
- At least one of
cost,usageandproductQuantityis still required. correlationIdmay not be empty: leave it out for a record without a key.- A record whose
correlationIdthe company already has replaces the stored record, as on v1 and v2. A field you leave out is cleared, so send records whole. - A
correlationIdrepeated within one upload is a400(DUPLICATE_CORRELATION_ID). - A record that would change a locked record is a
409(DATA_LOCKED), and nothing is stored. v1 and v2 skip such a record without saying so. Sending a record unchanged is always safe: it answers201with the stored record’s id. - The
201response lists every record’sindex,correlationIdandid, in request order. - A v2 field name sent to v3 is rejected as
UNKNOWN_PARAMETER, so a missed rename never goes unnoticed.
Posting journal lines
The posting-journal-line upload has no v3 version. Send each journal line to the v3 data-point upload instead:
| Posting journal line | v3 record |
|---|---|
lineDate, or transactionDate when there is none | date |
accountNo | category1 |
accountName | category2 |
amount + currencyCode | cost: { amount, currency } |
subEntryTitle | description |
entryNo, subEntryId | entryNo, subEntryId |
vendorOrgNo, vendorName | vendorOrgNumber, vendorName |
customerOrgNo, customerName | customerOrgNumber, customerName |
dimensions: [{ name, value }] | dimensions: [{ type, value }] |
- Set
correlationIdto{companyId}_{date}_{entryNo}_{subEntryId}, withdateasYYYY-MM-DDas in the table. That is the key the posting-journal-line upload gave each line, so re-sending a line through v3 updates it instead of adding a copy. transactionDate(whenlineDateis set),foreignAmount,foreignCurrencyCodeandexchangeRatehave no v3 field. None of them changed a calculation.- Don’t re-send journal lines dated in a locked period: they answer
409(DATA_LOCKED), because the stored line carries details v3 doesn’t send.
Partner benchmark
GET /partner/{partnerCode}/company/{erpCompanyId}/benchmark/real-estate has no replacement and stops working at the sunset.