Skip to main content
POST
/
v1
/
invoices
/
recalculate
Recalculate Invoices
const options = {
  method: 'POST',
  headers: {
    'x-api-key': '<x-api-key>',
    'x-organization-id': '<x-organization-id>',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({invoice_ids: {}, activity_categories: {}})
};

fetch('https://api.dcycle.io/v1/invoices/recalculate', options)
  .then(res => res.json())
  .then(res => console.log(res))
  .catch(err => console.error(err));
import requests

url = "https://api.dcycle.io/v1/invoices/recalculate"

payload = {
    "invoice_ids": {},
    "activity_categories": {}
}
headers = {
    "x-api-key": "<x-api-key>",
    "x-organization-id": "<x-organization-id>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
curl --request POST \
  --url https://api.dcycle.io/v1/invoices/recalculate \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <x-api-key>' \
  --header 'x-organization-id: <x-organization-id>' \
  --data '
{
  "invoice_ids": {},
  "activity_categories": {}
}
'
{
  "invoice_ids": {},
  "count": 123,
  "processing_job_ids": {}
}

Recalculate Invoices

Enqueue EF-config recalculation for a set of invoices by activity category. The endpoint returns 202 Accepted immediately and creates one ProcessingJob per requested activity category. Invoices remain in their current status while jobs are processing.
All invoice IDs must belong to the caller’s organization. IDs that don’t match are silently dropped. All surviving invoices must share a single supported type (heat, electricity, or recharge) unless only cross-type categories (e.g. energy) are requested.

Request

Headers

x-api-key
string
required
Your API key for authenticationExample: sk_live_1234567890abcdef
x-organization-id
string
required
Your organization UUIDExample: ff4adcc7-8172-45fe-9cf1-e90a6de53aa9

Body Parameters

invoice_ids
array of strings
required
One or more invoice UUIDs to recalculate. Must be non-empty.Example: ["a1b2c3d4-...", "e5f6g7h8-..."]
activity_categories
array of strings
required
Activity categories to recalculate. Must be non-empty. Duplicates are silently deduplicated.Allowed values:
  • stationary — Scope 1 stationary combustion (heat invoices)
  • stationary_generation — Scope 3 heat generation (heat invoices)
  • electricity — Scope 2 market-based electricity
  • electricity_location_based — Scope 2 location-based electricity
  • electricity_generation — Scope 3 electricity generation
  • electricity_transmission_and_distribution — Scope 3 T&D losses
  • energy — General energy category (cross-type, heat and electricity only)
  • recharge — Scope 1 fugitive emissions from refrigerants (recharge invoices)
Example: ["electricity", "electricity_location_based"]

Response

invoice_ids
array of strings
UUIDs of the invoices that were enqueued (after organization scoping).
count
integer
Number of invoices enqueued.
processing_job_ids
array of strings
One ProcessingJob UUID per dispatched activity category.

Example

curl -X POST "https://api.dcycle.io/v1/invoices/recalculate" \
  -H "x-api-key: ${DCYCLE_API_KEY}" \
  -H "x-organization-id: ${DCYCLE_ORG_ID}" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice_ids": [
      "550e8400-e29b-41d4-a716-446655440000",
      "660e8400-e29b-41d4-a716-446655440001"
    ],
    "activity_categories": ["electricity", "electricity_location_based"]
  }'
import requests
import os

response = requests.post(
    "https://api.dcycle.io/v1/invoices/recalculate",
    headers={
        "x-api-key": os.getenv("DCYCLE_API_KEY"),
        "x-organization-id": os.getenv("DCYCLE_ORG_ID"),
        "Content-Type": "application/json",
    },
    json={
        "invoice_ids": [
            "550e8400-e29b-41d4-a716-446655440000",
            "660e8400-e29b-41d4-a716-446655440001",
        ],
        "activity_categories": ["electricity", "electricity_location_based"],
    },
)

result = response.json()
print(f"Enqueued {result['count']} invoices, jobs: {result['processing_job_ids']}")
const axios = require('axios');

const response = await axios.post(
  'https://api.dcycle.io/v1/invoices/recalculate',
  {
    invoice_ids: [
      '550e8400-e29b-41d4-a716-446655440000',
      '660e8400-e29b-41d4-a716-446655440001',
    ],
    activity_categories: ['electricity', 'electricity_location_based'],
  },
  {
    headers: {
      'x-api-key': process.env.DCYCLE_API_KEY,
      'x-organization-id': process.env.DCYCLE_ORG_ID,
      'Content-Type': 'application/json',
    },
  }
);

console.log(`Enqueued ${response.data.count} invoices`);
console.log(`Job IDs: ${response.data.processing_job_ids.join(', ')}`);

Successful Response

{
  "invoice_ids": [
    "550e8400-e29b-41d4-a716-446655440000",
    "660e8400-e29b-41d4-a716-446655440001"
  ],
  "count": 2,
  "processing_job_ids": [
    "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "b2c3d4e5-f6a7-8901-bcde-f12345678901"
  ]
}

Common Errors

401 Unauthorized

Cause: Missing or invalid API key
{"detail": "Invalid API key for organization", "code": "INVALID_API_KEY"}

403 Forbidden

Cause: The authenticated user is not a member of the organization
{"detail": "Logged User is not Member of Organization", "code": "LOGGED_USER_NOT_MEMBER"}

400 Bad Request

Cause: Invoices span multiple types
{
  "detail": "Mixed invoice types are not supported",
  "code": "MIXED_INVOICE_TYPES"
}
Cause: Activity category doesn’t match invoice type
{
  "detail": "Activity category 'recharge' is not compatible with invoice type 'electricity'",
  "code": "ACTIVITY_CATEGORY_MISMATCH"
}

List Invoices

Retrieve invoices to find IDs for recalculation

Calculation Steps

View the emission calculation breakdown

Invoice Totals

Get aggregated totals after recalculation

Get Invoice

Check updated emissions on a single invoice