Jump to section
Getting Started

Overview & Base URLs

Base App
Auth, organizations, users & gateway routes
{baseURL}/api
Network Service
Wallets, tokens, transfers, retirements
{baseURL}/api/network
RNG Production
Batch creation, measurement & minting
{baseURL}/api/rng-production
Core API

Authentication

POST /auth/initiate-login

Optional pre-login step. Returns which login methods are available for an email address without revealing whether it exists.

Request Body
{
  "email": "user@example.com"
}
Returns
{
  "password": true,
  "tfa": "totp"   // omitted when no TFA is active
}
POST /auth/login

Primary login. Returns tokens used for all subsequent authenticated requests.

Request Body
{
  "email": "user@example.com",
  "secret": "your-password",
  "tfaCode": "123456"   // required when account uses TFA
}
Returns
{
  "accessToken": "<JWT>",
  "accessTokenExpiresAt": 1735000000,
  "refreshToken": "<refreshToken>",
  "refreshTokenExpiresAt": 1737000000
}
POST /auth/refresh-token

Refresh the current access token. Also accessible as GET /auth/refresh-token.

Headers
x-refresh-token: <refreshToken>
Returns
// Same shape as POST /auth/login
POST /auth/logout

Terminate the current session. Revokes the refresh state for the current user session.

Request Body
{}
Core API

User

POST /user/profile/organization

Fetches a combined view of the caller's user record, organizations, and organization profiles. Use this to resolve profileId values required by downstream requests. Also accessible as GET /user/profile/organization.

Optional Header
x-active-organization-id: <organizationId>  // pin context when user belongs to multiple orgs
Returns
{
  "user": { /* signed-in user record + permissions */ },
  "organizations": [ /* orgs the user can access */ ],
  "profiles": [ /* organization profiles tied to those orgs */ ]
}
POST /user/change-password

Change the current user's password.

Request Body
{
  "password": "new-password",
  "oldPassword": "current-password"
}
Core API

Two-Factor Authentication

POST/two-factor-auth/create

Create a two-factor authentication method for the current user.

{
  "password": "current-password",
  "type": "totp"
}
POST/two-factor-auth/activate

Activate a previously created two-factor authentication method.

{
  "tfaCode": "123456"
}
POST/two-factor-auth/disable

Disable a two-factor authentication method.

{
  "tfaId": "<tfaId>",
  "password": "current-password"
}
POST/two-factor-auth/remove

Remove a two-factor authentication method.

{
  "tfaId": "<tfaId>",
  "password": "current-password"
}
GET/two-factor-auth/list

List configured two-factor authentication methods for the current user.

Core API

Organizations & Profiles

GET/organization/list

List organizations available to the authenticated user.

Returns
{
  "organizations": [ /* array of orgs the caller may access (RBAC-derived) */ ]
}
POST/organization-profile/api-key/create

Create an API key for a given organization profile.

{
  "profileId": "<profileId>"
}
POST/organization-profile/api-key/list

List API keys for a given organization profile.

{
  "profileId": "<profileId>"
}
POST/organization-profile/flag/set

Set or remove feature flags on an organization profile.

{
  "profileId": "<profileId>",
  "flags": [
    { "flag": "GHGP", "value": true },
    { "flag": "BOOK_AND_CLAIM", "value": null }
  ]
}
Core API

Organization Users

POST/organization-user/list

List users associated with the current organization.

{}
POST/organization-user/invite-user

Invite a user to an organization.

{
  "organizationId": "<organizationId>",
  "email": "new.user@example.com",
  "firstName": "First",
  "lastName": "Last"
}
POST/organization-user/resend-user-invitation

Resend an invitation for an existing organization user.

{
  "organizationId": "<organizationId>",
  "userId": "<userId>"
}
POST/organization-user/set-organization-role

Assign organization-level roles to a user.

{
  "organizationId": "<organizationId>",
  "userId": "<userId>",
  "defaultRoleCodes": ["default.organization.admin"],
  "roleCodes": ["basic.admin"]
}
POST/organization-user/set-organization-profile-role

Assign organization-profile-level roles to a user.

{
  "profileId": "<profileId>",
  "userId": "<userId>",
  "roleCodes": ["basic.admin"]
}
Core API

Business Locations

POST/business-location/create

Create a business location associated with an organization profile.

{
  "profileId": "<profileId>",
  "name": "Location Name",
  "type": "buying",
  "basin": "Permian",
  "isDirectConnection": false,
  "preferredSuppliers": ["Antero"],
  "coordinates": {
    "latitude": 31.972096,
    "longitude": -83.760640
  },
  "address": {
    "country": "US"
  }
}
POST/business-location/update

Update a business location.

{
  "_id": "<businessLocationId>",
  "basin": "Permian",
  "isDirectConnection": true,
  "preferredSuppliers": ["QB"],
  "name": "Updated Location",
  "address": {
    "country": "US",
    "state": "NY"
  },
  "status": "active"
}
POST/business-location/details

Return details for a business location.

{
  "_id": "<businessLocationId>"
}
POST/business-location/list

List business locations for a given profile.

{
  "profileId": "<profileId>"
}
Network API

File Uploads & Downloads

POST/network/file/temporary-upload

Upload up to 5 files (10 MB each). Use the returned _id values as temporary document references in subsequent request bodies.

Request
Content-Type: multipart/form-data
Field name: files  (repeat for each file, up to 5)
Returns
{
  "files": [
    { "_id": "<uuid>", /* ...file record */ }
  ]
}
GET/network/file/download/:id/:fileName

Download a file by UUID. Caller must own the file or have permission on the network transaction it belongs to. Returns 403 otherwise. Non-PDF files receive Content-Disposition: attachment.

Network API

Energy Wallet

GET/energy-wallet/list

List energy wallets accessible to the caller's producer / base profiles.

Returns
{
  "wallets": [ /* array of energy wallets */ ]
}
POST/energy-wallet/summary/accounts

Aggregated account balance summary for a wallet. Supports filtering by energy type, token type, status, vintage year/month, carbon intensity, and more.

{
  "walletId": "<walletId>",
  "isPublic": false,
  "energyType": "natural-gas",   // natural-gas | natural-gas-pipeline | methanol | renewable-natural-gas | solid-fuel
  "tokenType": "QET",            // QET | CET
  "status": "enabled",           // enabled | disabled
  "year": 2024,
  "month": 6,
  "maxCarbonIntensity": 10.5,
  "uom": "MMBtu"
}
POST/energy-wallet/account/list

Paginated list of energy account summaries in a wallet. Accepts the same filter fields as summary/accounts.

{
  "walletId": "<walletId>",
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "energyAccounts": [ /* paginated array of account summaries */ ]
}
GET/energy-wallet/account/details/:id

Return a single energy account summary by UUID.

Returns
{
  "energyAccount": { /* energy account summary record */ }
}
POST/energy-wallet/account/set-public

Publish or unpublish energy accounts for market listing, setting a price for public accounts.

{
  "walletId": "<walletId>",
  "accountIds": ["<accountId>"],
  "isPublic": true,
  "price": { "amount": 5.00, "currency": "USD" }
}
POST/energy-wallet/transaction/list

Paginated list of ledger transactions for a wallet.

{
  "walletId": "<walletId>",
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "transactions": [ /* paginated ledger transactions */ ]
}
Network API

Market Accounts

POST/market/account/list

Paginated list of public energy accounts. All filter fields are optional and can be combined freely.

{
  "energyType": "renewable-natural-gas",
  "tokenType": "QET",
  "year": 2024,
  "month": 6,
  "standardType": "OGCI",
  "basin": "Permian",
  "maxCarbonIntensity": 10.5,
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "energyAccounts": [ /* public energy account summaries */ ]
}
GET/market/account/details/:id

Return a single public energy account by UUID. Only public accounts are returned.

Returns
{
  "energyAccount": { /* public energy account summary */ }
}
POST/market/account/summary

Aggregated balance summary for a specified owner's public accounts.

{
  "ownerId": "<ownerId>"
}
Network API

Minting Contracts

POST/minting/create
{
  "name": "My Minting Contract",
  "referenceIds": ["<energyAssetId>"],   // required, non-empty
  "operationContractType": "natural_gas_produce_minting",  // or natural_gas_transportation_minting
  "workflowId": "<workflowId>",           // optional
  "buyer": { "profileId": "<profileId>", "walletId": "<walletId>" }  // optional
}
Returns
{
  "contract": { /* created minting contract record */ }
}
POST/minting/list

All filter fields are optional. Also accessible as POST /network/minting/list via the gateway.

{
  "status": "draft",        // draft | awaiting_approval | processed | canceled
  "energyType": "natural-gas",
  "tokenType": "QET",       // QET | CET
  "operationContractType": "natural_gas_produce_minting",
  "issuerProfileId": "<profileId>",
  "buyerProfileId": "<profileId>",
  "createdAtFrom": "2024-01-01T00:00:00Z",
  "createdAtTo": "2024-12-31T23:59:59Z",
  "paginate": { "skip": 0, "limit": 100 }  // limit max 2500
}
Returns
{
  "contracts": [ /* minting contract summaries */ ]
}
GET/minting/details/:id

Return the full minting contract record by UUID.

Returns
{
  "contract": { /* full minting contract record */ }
}
POST/minting/details/cohort

Aggregated cohort (token-level) breakdown for a minting contract.

{
  "_id": "<mintingContractId>",
  "energyTokenId": "<energyTokenId>"
}
Returns
{
  "cohortSummary": { /* token-level summary */ }
}
POST/minting/remove
{
  "_id": "<mintingContractId>"
}
Returns
{
  "contract": { /* removed minting contract record */ }
}
Network API

Network Transactions

POST/network-transaction/push/create

Initiate a push transaction. Caller owns the source wallet.

{
  "fromProfileId": "<profileId>",
  "fromWalletId": "<walletId>",  // optional; resolved from profile when omitted
  "toProfileId": "<profileId>",
  "toWalletId": "<walletId>",    // optional
  "isSecure": false               // true = two-step secure push
}
Returns
{
  "networkTransaction": { /* created transaction record */ }
}
POST/network-transaction/push/lock

Lock energy accounts for an existing push transaction.

{
  "_id": "<transactionId>",
  "quantity": 100,
  "filter": {
    "energyType": "natural-gas",
    "tokenType": "QET",
    "isPublic": false
    // ...additional MarketAccountFilter fields
  },
  "forcePrice": { "amount": 5.00, "currency": "USD" }  // optional
}
POST/network-transaction/pull/create

Initiate a pull transaction. Caller owns the destination wallet.

{
  "fromProfileId": "<profileId>",
  "fromWalletId": "<walletId>",  // optional
  "toProfileId": "<profileId>",
  "toWalletId": "<walletId>"     // optional
}
Returns
{
  "networkTransaction": { /* created transaction record */ }
}
POST/network-transaction/pull/lock

Lock energy accounts for a pull transaction. Only public accounts are eligible — isPublic: true is enforced server-side.

{
  "_id": "<transactionId>",
  "quantity": 100,
  "filter": {
    "energyType": "natural-gas",
    "tokenType": "QET"
  }
}
POST/network-transaction/list
{
  "walletId": "<walletId>",
  "status": "draft",          // draft | awaiting_approval | processed | canceled
  "transactionType": "push",  // push | pull
  "energyType": "natural-gas",
  "iAmSigner": true,          // when true: only returns transactions where caller is a signer
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "networkTransactions": [ /* paginated list; fee fields cleared for non-payer callers */ ]
}
POST/network-transaction/details
{
  "_id": "<transactionId>"
}
Returns
{
  "networkTransaction": { /* full transaction record */ }
}
POST/network-transaction/details/cohort
{
  "_id": "<transactionId>",
  "energyTokenId": "<energyTokenId>"
}
Returns
{
  "cohortSummary": { /* token-level summary */ }
}
POST/network-transaction/update
{
  "_id": "<transactionId>",
  "contractId": "string",       // optional
  "description": "string",      // optional
  "files": [{ /* FileDto */ }],       // optional; files to attach
  "removeFiles": [{ /* FileDto */ }]  // optional; files to detach
}
Returns
{
  "networkTransaction": { /* updated transaction record */ }
}
POST/network-transaction/unlock
{
  "_id": "<transactionId>"
}
POST/network-transaction/remove
{
  "_id": "<transactionId>"
}
POST/network-transaction/cancel
{
  "_id": "<transactionId>",
  "description": "Cancellation reason"  // optional
}
POST /network-transaction/sign ⚠ TFA Required

Signs and submits the transaction for processing. The access token must reflect a completed TFA step (usedTfa: true on JWT payload).

{
  "_id": "<transactionId>"
}
Network API

Intra-Transfer

POST/intra-transfer/create

Creates a new intra-transfer in draft status.

{
  "fromProfileId": "<profileId>",
  "fromWalletId": "<walletId>",  // optional
  "toProfileId": "<profileId>",
  "toWalletId": "<walletId>"     // optional
}
Returns
{
  "retirement": { /* created intra-transfer record (field name is "retirement") */ }
}
POST/intra-transfer/list
{
  "walletId": "<walletId>",
  "profileId": "<profileId>",    // optional
  "status": "draft",              // draft | awaiting_approval | processed | canceled
  "energyType": "natural-gas",
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "intraTransfers": [ /* paginated list */ ]
}
POST/intra-transfer/details
{
  "_id": "<intraTransferId>"
}
Returns
{
  "intraTransfer": { /* full record */ }
}
POST/intra-transfer/details/cohort
{
  "_id": "<intraTransferId>",
  "energyTokenId": "<energyTokenId>"
}
Returns
{
  "cohortSummary": { /* token-level summary */ }
}
POST/intra-transfer/lock
{
  "_id": "<intraTransferId>",
  "quantity": 100,
  "filter": { /* energy account selection criteria */ }
}
POST/intra-transfer/unlock
{
  "_id": "<intraTransferId>"
}
POST/intra-transfer/remove

Delete a draft intra-transfer.

{
  "_id": "<intraTransferId>"
}
POST /intra-transfer/sign ⚠ TFA Required

Signs and submits the intra-transfer. The access token must reflect a completed TFA step (usedTfa: true on JWT payload).

{
  "_id": "<intraTransferId>"
}
Network API

Consumption

POST/consumption/create
{
  "profileId": "<profileId>",
  "walletId": "<walletId>",  // optional
  "name": "Consumption label"
}
Returns
{
  "consumption": { /* created consumption record */ }
}
POST/consumption/list
{
  "walletId": "<walletId>",
  "status": "draft",
  "energyType": "natural-gas",
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "consumptions": [ /* paginated list */ ]
}
POST/consumption/details
{
  "_id": "<consumptionId>",
  "fullCohorts": true  // optional; includes full cohort breakdown when true
}
Returns
{
  "consumption": { /* record with associated accounts */ }
}
POST/consumption/details/cohort
{
  "_id": "<consumptionId>",
  "energyTokenId": "<energyTokenId>"
}
Returns
{
  "cohortSummary": { /* token-level summary */ }
}
POST/consumption/lock
{
  "_id": "<consumptionId>",
  "quantity": 100,
  "filter": {
    "ownerId": "<ownerId>",   // optional
    "isPublic": false          // optional; plus other MarketAccountFilter fields
  }
}
POST/consumption/unlock
{
  "_id": "<consumptionId>"
}
POST/consumption/remove
{
  "_id": "<consumptionId>"
}
POST /consumption/sign ⚠ TFA Required

Signs and submits the consumption record. The access token must reflect a completed TFA step.

{
  "_id": "<consumptionId>"
}
Network API

Retirement

POST/retirement/create

Creates a new retirement in draft status.

{
  "profileId": "<profileId>",
  "walletId": "<walletId>",  // optional
  "name": "Retirement label"
}
Returns
{
  "retirement": { /* created retirement record */ }
}
POST/retirement/list
{
  "walletId": "<walletId>",
  "status": "draft",
  "energyType": "natural-gas",
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "retirements": [ /* paginated list */ ]
}
POST/retirement/details
{
  "_id": "<retirementId>",
  "fullCohorts": true  // optional
}
Returns
{
  "retirement": { /* record with associated energy accounts */ }
}
POST/retirement/details/cohort
{
  "_id": "<retirementId>",
  "energyTokenId": "<energyTokenId>"
}
Returns
{
  "cohortSummary": { /* token-level summary */ }
}
POST/retirement/lock
{
  "_id": "<retirementId>",
  "quantity": 100,
  "filter": { /* energy account selection criteria */ }
}
POST/retirement/unlock
{
  "_id": "<retirementId>"
}
POST/retirement/update

Update metadata fields (beneficiary, purpose, reporting period).

{
  "_id": "<retirementId>",
  "purpose": "Scope 1 emissions offset",
  "additionalContext": "Q4 2024",
  "beneficiary": { /* RetirementBeneficiaryDto */ }  // set to null to clear
}
POST/retirement/remove

Delete a draft retirement.

{
  "_id": "<retirementId>"
}
POST /retirement/sign ⚠ TFA Required

Signs and submits the retirement. Triggers certificate generation. The access token must reflect a completed TFA step (usedTfa: true on JWT payload).

{
  "_id": "<retirementId>"
}
GET/retirement/certificate/:id

Download the PDF retirement certificate once the retirement is processed.

Returns
Content-Type: application/pdf  (streaming)
POST/retirement/lookup

Resolve the retirement record associated with an on-chain token identifier within a specific wallet.

{
  "tokenId": "<energyTokenId>",
  "walletId": "<walletId>"
}
Natural Gas Production API

Production Sources (Wells)

POST/production-source/upload-wells

Register wells for an organization from an uploaded spreadsheet. Pass the _id returned by the temporary file upload as document._id.

Request Body
{
  "organizationId": "<organizationId>",
  "document": { "_id": "<temporaryFileUuid>" }
}
POST/production-source/list

List registered wells. All filters are optional — combine basin, identifier, or identifierIds to narrow results.

{
  "basin": "<basin>",
  "identifier": "99",                 // optional prefix match
  "identifierIds": ["99-001-00499"]   // optional exact API-14 well ids
}
POST/production-source/set-status

Activate or deactivate one or more wells by identifier.

{
  "organizationId": "<organizationId>",
  "identifierIds": ["99-001-00498"],
  "status": "inactive"   // active | inactive
}
POST/production-source/remove

Remove one or more wells by identifier.

{
  "organizationId": "<organizationId>",
  "identifierIds": ["99-001-00498"]
}
Natural Gas Production API

Production Batches

POST/production-batch/create

Create a monthly production batch. Returns the batch record including its _id (used as batchId downstream).

{
  "document": { "_id": "<temporaryFileUuid>" },
  "year": 2024,
  "month": 11,
  "profileId": "<profileId>",
  "name": "B26_-_November_2024"
}
POST/production-batch/list/issuer

List batches visible to the caller acting as the issuer (producer). Body may be empty {} or carry filter fields.

{}
POST/production-batch/list/measurer

List batches visible to the caller acting as the measurer.

{}
POST/production-batch/list/verifier

List batches visible to the caller acting as the verifier.

{}
GET/production-batch/details/:id

Fetch full batch details, including its measurement workflows. Each workflow's _id is the workflowId used by measurement, sign-off, and minting requests.

POST/production-batch/remove
{
  "batchId": "<batchId>"
}
Natural Gas Production API

Measurement Batches

POST/measurement-batch/create

Create a measurement batch for a production batch. NGSI uses a measurer and a verifier; OGCI may be created with a measurer alone. Returns the workflow (its _id is the workflowId).

Request Body — NGSI
{
  "batchId": "<batchId>",
  "standardType": "NGSI",      // NGSI | OGCI
  "measurer": "validere",
  "verifier": "spirit"
}
Request Body — OGCI
{
  "batchId": "<batchId>",
  "standardType": "OGCI",
  "measurer": "project-canary"
}
POST/measurement-batch/create-verify-only-batch

Create an OF-style verify-only measurement batch (no measurer step — goes straight to verifier sign-off). Reference the uploaded data file as document._id.

{
  "document": { "_id": "<temporaryFileUuid>" },
  "profileId": "<profileId>",
  "name": "OF1-_2026",
  "verifier": "demo_verifier"
}
POST/measurement-batch/call-measurer

Trigger the assigned measurer to perform measurement for the workflow.

{
  "workflowId": "<workflowId>"
}
GET/measurement-batch/export/:id

Export the measurement data for a measurement batch (by measurement-batch id).

POST /measurement-batch/approve ⚠ TFA likely

Producer sign-off. The producer (issuer) approves the completed, measured-and-verified workflow, clearing it for minting. Sign-off steps are likely TFA-gated — confirm against your environment.

{
  "workflowId": "<workflowId>",
  "profileId": "<profileId>"
}
POST/measurement-batch/reject
{
  "workflowId": "<workflowId>",
  "description": "reason for rejection"
}
POST/measurement-batch/remove
{
  "profileId": "<profileId>",
  "workflowId": "<workflowId>"
}
Natural Gas Production API

Measurer Integration (GraphQL)

How measurement data flows

After a measurement batch is created and the measurer is engaged, the measurer reads the batch's assets, computes emissions/standard data, and writes the results back into EarnDLT through the /graphql endpoint. The trigger differs by measurer; the write-back is the same GraphQL mutation for both:

MeasurerStandardTriggerWrite-back
Project CanaryOGCIEarnDLT calls Project Canary (POST /measurement-batch/call-measurer). Project Canary collects and collates the data, then calls back into EarnDLT’s /graphql.AddStandard mutation
ValidereNGSIEarnDLT does not call Validere’s API — an email notification is sent. Validere collects and collates the data, then calls EarnDLT’s /graphql.AddStandard mutation

In both cases the measurer first reads the batch and its assets with GetMeasurementBatch, then writes standard/emissions results per asset with multipleAssetStandardAdd (AddStandard).

POST/graphqlQuery

GetMeasurementBatch — read a measurement batch and its assets (production data, pollutant source, energy quantities) to know what to measure. assetOptions paginates the assets.

Query
query GetMeasurementBatch($id: UUID!, $assetOptions: EnergyAssetsFilterInputType!) {
  measurementBatch(id: $id) {
    _id
    status
    identifier
    entriesCount
    entriesReceived
    entriesResolved
    error
    createdAt
    updatedAt
    assets(options: $assetOptions) {
      count page pages perPage total
      results {
        _id status tags createdAt updatedAt
        productionBatchData {
          id name profileId createdBy createdAt
          startDate endDate productionDays
          methaneConcentration btuFactor
        }
        pollutantSource {
          basin category field name number pad
          identifier { format id }
        }
        energy {
          stage type
          quantity { amount uom }
        }
      }
    }
  }
}
Variables
{
  "id": "<measurementBatchId>",
  "assetOptions": { "page": 1, "perPage": 10000 }
}
POST/graphqlMutation

AddStandard (multipleAssetStandardAdd) — write measured standard/emissions results back to EarnDLT, one entry per asset. The update.items[] envelope is shared; each item carries the asset _id, an optional scope2 electricity block, and a standard body whose exact shape is supplied by the measurer and varies by standard (OGCI vs NGSI — see below).

Mutation
mutation AddStandard($update: EnergyAssetStandardAddItems!) {
  multipleAssetStandardAdd(update: $update) {
    _id
  }
}
Variables — shared envelope
{
  "update": {
    "items": [
      {
        "_id": "<energyAssetId>",
        "scope2": {
          "usage":     { "value": 0, "uom": "MWH" },
          "emissions": { "value": 0, "uom": "KG_PER_MMBTU" },
          "source":    { "name": "EPA eGrid Emission Factors",
                         "url": "https://www.epa.gov/egrid" }
        },
        "standard": { /* standard-specific — see NGSI / OGCI below */ }
      }
    ]
  }
}

NGSI standard body (Validere)

Representative shape — a carbon-intensity summary plus per-pollutant emissions carrying both reported values and the measurer’s raw measurerData.

"standard": {
  "standard": { "name": "NGSI", "url": "https://www.ngsi.com/" },
  "carbonIntensity": {
    "value": 2.765e-06, "uom": "kg CO2e/MMBtu",
    "label": "CO2e of Methane Intensity",
    "formula": "Attributed kgs of methane emitted as CO2e per MMbtu sold"
  },
  "emissions": [
    {
      "pollutant": "CH4",
      "scope": 1,
      "pollutantIntensity": { "value": 5.24e-09, "uom": "percentage", "label": "CH4 Intensity" },
      "pollutantMass":      { "value": 9.88e-08, "uom": "kg/MMBtu",   "label": "CH4 Mass" },
      "carbonIntensity":    { "value": 2.765e-06, "uom": "kg CO2e/MMBtu",
                              "factor": "GWP100(AR5)", "label": "CO2e of Methane Intensity" },
      "measurerData": {
        "pollutantMass":      { "value": 0.001696, "uom": "kg",         "label": "NGSI CH4 Mass" },
        "pollutantIntensity": { "value": 5.82e-07, "uom": "percentage", "label": "NGSI CH4 Intensity" }
      },
      "methodology": {
        "granularity": "pollutantSource",
        "name": "NGSI Methane Emissions Intensity Protocol Version 2.0",
        "type": "measured"
      }
    }
  ]
}

Send one items[] entry per asset. Fields are trimmed here for readability; the measurer supplies the full payload.

OGCI standard body (Project Canary)

Representative shape — emissions plus a metadata[] array of OGCI rating fields (medal, land / air / community / water scores, methane intensity, vintage, protocol).

"standard": {
  "standard": { "name": "OGCI", "url": "https://www.ogci.com" },
  "carbonIntensity": null,
  "emissions": [
    {
      "pollutant": "CH4",
      "scope": 1,
      "carbonIntensity": null,
      "pollutantIntensity": { "value": 0.0144, "uom": "percentage" },
      "pollutantMass": null,
      "methodology": { "granularity": "BASIN", "name": "US EPA", "type": "ESTIMATED" },
      "metadata": [
        { "name": "METHODOLOGY_REFERENCE", "type": "string", "value": "TW-2021-v10.2" }
      ],
      "source": { "frequency": "ANNUAL", "name": "n/a",
                  "measurementTechnology": "n/a", "id": "" }
    }
  ],
  "metadata": [
    { "name": "RATING_MEDAL",             "type": "string", "value": "Gold" },
    { "name": "RATING_LAND_SCORE",        "type": "number", "value": "136" },
    { "name": "RATING_AIR_SCORE",         "type": "number", "value": "137" },
    { "name": "RATING_COMMUNITY_SCORE",   "type": "number", "value": "138" },
    { "name": "RATING_WATER_SCORE",       "type": "number", "value": "139" },
    { "name": "RATING_METHANE_INTENSITY", "type": "number", "value": "0.0144" },
    { "name": "RATING_VINTAGE",           "type": "number", "value": "2022" },
    { "name": "RATING_METHANE_INTENSITY_PROTOCOL", "type": "string", "value": "Ogci" }
    /* ...plus RATING_START_DATE / END_DATE, PERFORMANCE_SCORE,
       METHANE_INTENSITY_GRANULARITY / _EMISSION_PROTOCOL, each with a source{} */
  ],
  "rawJSON": "{}"
}

Each metadata[] entry also carries a source object (frequency, id, measurementTechnology, name); omitted above for brevity.

Natural Gas Production API

Measurer Sign-off

POST /measurer/approve ⚠ TFA likely

Measurer approves the measurement for the workflow.

{
  "workflowId": "<workflowId>",
  "profileId": "<measurerProfileId>"
}
POST/measurer/reject
{
  "workflowId": "<workflowId>",
  "profileId": "<measurerProfileId>",
  "description": "reason for rejection"
}
Natural Gas Production API

Verifier Sign-off

POST /verifier/approve ⚠ TFA likely

Verifier approves the workflow with a signed verification statement and attached evidence.

{
  "workflowId": "<workflowId>",
  "profileId": "<verifierProfileId>",
  "files": [
    { "_id": "<verificationFileUuid>" }
  ],
  "verificationStatement": {
    "assuranceLevel": "limited",        // e.g. limited | reasonable
    "opinionType": "unmodified",
    "reference": "VER-2025-00234"
  }
}
POST/verifier/reject
{
  "workflowId": "<workflowId>",
  "profileId": "<verifierProfileId>",
  "description": "reason for rejection"
}
Natural Gas Production API

Batch Minting

POST /production-batch-minting/send-to-network ⚠ TFA likely
{
  "batchId": "<batchId>",
  "tokenType": "QET",
  "scope": { "workflowId": "<workflowId>" }
}
POST/production-batch-minting/create-minting-contract

Create a minting contract from the approved batch/workflow. Use the returned contract._id as mintingContractId in the next step.

{
  "batchId": "<batchId>",
  "name": "minting-contract-1753461925",
  "scope": { "workflowId": "<workflowId>" }
}
POST /production-batch-minting/mint-tokens ⚠ TFA likely

Mint the natural-gas QET tokens (NGSI / OGCI / OF, per the workflow's standard).

{
  "mintingContractId": "<mintingContractId>"
}
RNG Production API

File Uploads & Downloads

POST/file/temporary-upload

Upload up to 5 files (10 MB each). The returned _id is used as a document._id when creating a production batch.

Request
Content-Type: multipart/form-data
Field name: files  (repeat for each file, up to 5)
Returns
{
  "files": [
    { "_id": "<uuid>", /* ...file record */ }
  ]
}
POST/file/list

List files attached to a specific entity.

{
  "ownerType": "production_batch",  // user | organization | organization_profile | energy_asset
                                    // production_batch | batch_workflow | network_transaction | retirement
  "ownerId": "<entityId>"
}
Returns
{
  "files": [ /* file records for the specified owner */ ]
}
GET/file/download/:id/:fileName

Download a file by UUID. Caller must have permission on the file's owner entity. Returns 403 otherwise.

GET/token-account/energy-wallet/file/:account_id/:id/:filename

Download a file attached to a specific energy-wallet token account.

GET/token-account/market/file/:account_id/:id/:filename

Download a file attached to a specific market token account.

RNG Production API

Production Batches

POST/rng-production-batch/create

Create a new RNG production batch. Upload the document first using POST /file/temporary-upload and pass the returned _id here.

{
  "name": "Batch label",
  "profileId": "<profileId>",
  "energyType": "renewable-natural-gas",  // renewable-natural-gas | solid-fuel
  "tokenType": "QET",                     // QET | CET
  "document": { "_id": "<temporaryFileUuid>" }
}
Returns
{
  "batch": { /* production batch record including _id */ }
}
POST/rng-production-batch/details

Fetch full batch details including workflows. Use the relevant item in batch.workflows — its _id is the workflowId required by measurement and minting requests.

{
  "_id": "<batchId>"
}
Returns
{
  "batch": { /* batch with workflows, assets, and attached files */ }
}
POST/rng-production-batch/list/issuer
{
  "profileId": "<profileId>",               // optional; defaults to all caller profiles
  "boundaryIdentifierId": "facility-id",    // optional
  "energyType": "renewable-natural-gas",    // optional
  "vintageDateFrom": "2024-01-01T00:00:00Z",
  "vintageDateTo": "2024-12-31T23:59:59Z",
  "paginate": { "skip": 0, "limit": 50 }
}
Returns
{
  "batches": [ /* paginated list visible to caller's issuer profiles */ ]
}
POST/rng-production-batch/reject
{
  "batchId": "<batchId>"
}
POST/rng-production-batch/remove
{
  "batchId": "<batchId>"
}
RNG Production API

Measurement Batches

POST/rng-measurement-batch/add-files

Attach files to the measurement batch step. The files array is required and must be non-empty.

{
  "profileId": "<profileId>",
  "workflowId": "<workflowId>",
  "description": "Optional note",
  "files": [
    { "_id": "<temporaryFileUuid>" }
  ]
}
POST/rng-measurement-batch/remove-files
{
  "profileId": "<profileId>",
  "workflowId": "<workflowId>",
  "description": "Optional note",
  "files": [
    { "_id": "<fileUuid>" }
  ]
}
POST /rng-measurement-batch/approve ⚠ TFA Required

Approve the measurement batch step. The access token must reflect a completed TFA step.

{
  "profileId": "<profileId>",
  "workflowId": "<workflowId>",
  "description": "Optional approval note"
}
POST/rng-measurement-batch/reject
{
  "profileId": "<profileId>",
  "workflowId": "<workflowId>",
  "description": "Optional rejection reason"
}
POST/rng-measurement-batch/remove
{
  "profileId": "<profileId>",
  "workflowId": "<workflowId>"
}
RNG Production API

Batch Minting

POST /rng-production-batch-minting/send-to-network ⚠ TFA Required
{
  "batchId": "<batchId>",
  "tokenType": "QET",
  "scope": { "workflowId": "<workflowId>" }  // optional
}
Returns
{
  "energyAssets": [ /* assets sent to the network */ ]
}
POST/rng-production-batch-minting/create-minting-contract

Create a minting contract from an approved batch. Use the returned contract._id as mintingContractId in the next step.

{
  "name": "Contract label",
  "batchId": "<batchId>",
  "scope": { "workflowId": "<workflowId>" }  // optional
}
Returns
{
  "contract": { /* minting contract; use contract._id as mintingContractId */ }
}
POST /rng-production-batch-minting/mint-tokens ⚠ TFA Required
{
  "mintingContractId": "<contractId>"
}
Returns
{
  "energyAssets": [ /* minted assets */ ]
}
Resources

Example: RNG

A complete walkthrough of the RNG production flow on staging — from login through minting tokens. An RNG production batch carries a data file and moves through a measurement step (add files → approve) before minting; each batch exposes a workflowId (from batch.workflows) that the measurement and minting requests reference. Files are uploaded first via POST /file/temporary-upload and referenced by their returned _id.

Prerequisites
  • A test user with a password (and a 2FA device if that user has TFA enabled).
  • Steps 11, 12, and 14 require a session authenticated with TFA — unless DISABLE_REQUIRE_TFA is set in the environment.
Authentication
1
Initiate login Optional

If the account uses two-factor authentication, this step triggers delivery of a code before you call login. Inspect the response to know whether you must supply tfaCode in the next step.

POST /auth/initiate-login
{ "email": "user@example.com" }
2
Primary login

Obtain accessToken and refreshToken. Send Authorization: Bearer <accessToken> on all following requests. Omit tfaCode when 2FA does not apply.

POST /auth/login
{ "email": "user@example.com", "secret": "your-password", "tfaCode": "123456" }

Organization context
3
List organizations

Discover which organizations the user can act under. Pick an organization ID from the response and send it as the x-active-organization-id header on all subsequent requests to scope permissions to that organization.

GET /organization/list
4
Resolve user profiles

Resolve issuer/producer profile UUIDs required for batch creation and measurement steps. Choose the profileId that corresponds to the role for your scenario.

POST /user/profile/organization

Discovery
5
List minting contracts Optional

Before or after creating a batch, inspect existing minting contracts. You can query without a workflowId to browse, or return here after step 9 with the resolved ID.

POST /network/minting/list
{ "workflowId": "<workflowId>", "paginate": { "skip": 0, "limit": 20 } }

File uploads
6
Upload LCA calculator spreadsheet (XLSX)

Stage the Excel file; the API returns temporary file metadata including _id.

POST /rng-production/file/temporary-upload
multipart/form-data · field: files = <your .xlsx>
💾 Save documentIdXlsx = files[0]._id → used in step 8
7
Upload supporting PDF

Stage the evidentiary PDF using the same endpoint.

POST /rng-production/file/temporary-upload
multipart/form-data · field: files = <your .pdf>
💾 Save documentIdPdf → used in step 10

Batch creation
8
Create RNG production batch

Create the batch from the LCA file uploaded in step 6.

POST /rng-production/rng-production-batch/create
{ "document": { "_id": "<documentIdXlsx>" }, "profileId": "<profileId>", "energyType": "renewable-natural-gas", "tokenType": "QET", "name": "my-batch-label" }
💾 Save batchId = batch._id → used in steps 9, 12, 13
9
Batch details → resolve workflowId

Load the batch and read the workflow identifier used by measurement and minting APIs. Use the relevant entry in batch.workflows — its _id is the workflowId for downstream calls. You can now repeat step 5 with this ID.

POST /rng-production/rng-production-batch/details
{ "_id": "<batchId>" }
💾 Save workflowId = batch.workflows[n]._id → used in steps 10–13

Measurement
10
Attach PDF to measurement batch

Link supporting documents to the measurement workflow.

POST /rng-production/rng-measurement-batch/add-files
{ "profileId": "<profileId>", "workflowId": "<workflowId>", "files": [{ "_id": "<documentIdPdf>" }] }
11
Approve measurement batch ⚠ TFA Required

Approve the measurement batch for the workflow. The access token must reflect a completed TFA step.

POST /rng-production/rng-measurement-batch/approve
{ "profileId": "<profileId>", "workflowId": "<workflowId>" }

Minting
12
Send batch to network ⚠ TFA Required

Push the approved batch toward on-network processing.

POST /rng-production/rng-production-batch-minting/send-to-network
{ "batchId": "<batchId>", "tokenType": "QET", "scope": { "workflowId": "<workflowId>" } }
13
Create minting contract

Create the minting contract for this batch and workflow.

POST /rng-production/rng-production-batch-minting/create-minting-contract
{ "batchId": "<batchId>", "name": "minting-contract-001", "scope": { "workflowId": "<workflowId>" } }
💾 Save mintingContractId = contract._id → used in step 14
14
Mint tokens ⚠ TFA Required

Execute the final mint for the contract created in step 13.

POST /rng-production/rng-production-batch-minting/mint-tokens
{ "mintingContractId": "<mintingContractId>" }
Natural Gas Production API

Example: Natural Gas Minting (NGSI)

End-to-end walkthrough for minting natural-gas QETs under the NGSI standard — from registering wells through minting. The measurement standard and the measurer/verifier parties are chosen when the measurement batch is created; the standard can be NGSI, OGCI, or (verify-only) OF, and this example uses NGSI. Creating the measurement batch returns a workflowId that threads through measurer, verifier, and producer sign-off and into minting. Files (wells, monthly data, verification evidence) are uploaded first via POST /file/temporary-upload and referenced by their returned _id.

Prerequisites
  • A producer (issuer) profile, plus measurer and verifier parties available for the chosen standard.
  • Wells registered for the organization (steps 1–2).
  • Sign-off and minting steps are likely TFA-gated — confirm against your environment.
Register Wells
1
Upload the wells file

Upload a spreadsheet of wells and receive a temporary file id. (Shared endpoint — see Network API File Uploads.)

POST/file/temporary-upload
Content-Type: multipart/form-data Field name: files → returns files[0]._id
2
Register the wells

Register the wells for your organization, referencing the uploaded file.

POST/production-source/upload-wells
{ "organizationId": "<organizationId>", "document": { "_id": "<fileId>" } }
Create the Monthly Batch
3
Upload the monthly data file

Upload the month's production data file; keep the returned _id.

POST/file/temporary-upload
Field name: files → returns files[0]._id
4
Create the production batch

Create the batch for a given month/year and profile. Save the returned batch _id as batchId.

POST/production-batch/create
{ "document": { "_id": "<fileId>" }, "year": 2024, "month": 11, "profileId": "<profileId>", "name": "B26_-_November_2024" }
Measurement
5
Create the measurement batch

Attach the NGSI standard and the measurer + verifier parties. Save the returned workflowId. (OGCI and OF variants exist — see the Natural Gas Production API reference.)

POST/measurement-batch/create
{ "batchId": "<batchId>", "standardType": "NGSI", "measurer": "validere", "verifier": "spirit" }
6
Notify the measurer

The measurer is engaged to run measurement for the workflow. For NGSI (Validere) an email notification is sent automatically — no API call is made. For OGCI (Project Canary), EarnDLT instead calls the measurer via POST /measurement-batch/call-measurer.

POST/measurement-batch/call-measurer — OGCI only
{ "workflowId": "<workflowId>" } // OGCI / Project Canary trigger
7
Measurer uploads the measured data

The measurer reads the batch assets with the GetMeasurementBatch query, then writes the measured standard/emissions results back per asset via the AddStandard (multipleAssetStandardAdd) GraphQL mutation. For NGSI (Validere) this is triggered by an email notification rather than an API call; for OGCI (Project Canary) it follows the call-measurer step. See Measurer Integration (GraphQL) for the full query and mutation.

POST/graphql — mutation AddStandard
mutation AddStandard($update: EnergyAssetStandardAddItems!) { multipleAssetStandardAdd(update: $update) { _id } } // variables: { "update": { "items": [ { "_id": "<energyAssetId>", "standard": { /* NGSI body */ } } ] } }
8
Measurer signs offTFA

The measurer approves the measurement result, advancing the workflow to verification.

POST/measurer/approve
{ "workflowId": "<workflowId>", "profileId": "<measurerProfileId>" }
Verification
9
Verifier signs offTFA

The verifier issues a verification statement, attaches evidence, and approves.

POST/verifier/approve
{ "workflowId": "<workflowId>", "profileId": "<verifierProfileId>", "files": [ { "_id": "<fileId>" } ], "verificationStatement": { "assuranceLevel": "limited", "opinionType": "unmodified", "reference": "VER-2025-00234" } }
Producer Sign-off & Minting
10
Producer signs offTFA

The producer (issuer) approves the measured-and-verified workflow, clearing it for minting.

POST/measurement-batch/approve
{ "workflowId": "<workflowId>", "profileId": "<profileId>" }
11
Send to networkTFA

Submit the approved batch/workflow to the network as QET.

POST/production-batch-minting/send-to-network
{ "batchId": "<batchId>", "tokenType": "QET", "scope": { "workflowId": "<workflowId>" } }
12
Create the minting contract

Create the minting contract; save the returned contract _id as mintingContractId.

POST/production-batch-minting/create-minting-contract
{ "batchId": "<batchId>", "name": "minting-contract-1753461925", "scope": { "workflowId": "<workflowId>" } }
13
Mint the QET tokensTFA

Mint the natural-gas QETs for the contract (NGSI / OGCI / OF per the workflow's standard).

POST/production-batch-minting/mint-tokens
{ "mintingContractId": "<mintingContractId>" }
Get Access

Request API Access

API access gives you programmatic control over organizations, business locations, emissions data, and token transactions on the EarnDLT network. Fill out the form below and our team will be in touch within one business day to provision credentials and walk you through onboarding.