# Introduction to Carbonmark API

Discover carbon credit inventory, retrieve pricing, and retire credits programmatically.

Carbonmark is designed for teams adding carbon credit purchasing and retirement to software products, internal tools, and customer workflows. Use the API to embed carbon credit discovery and retirement directly into your application without needing to manage credit inventory yourself.

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FWjdU0yXOIC8HTXXfnR7X%2Fimage.png?alt=media&amp;token=59c4afdd-f222-4695-8793-d4185bb5343c" alt=""><figcaption></figcaption></figure>

## What you can do

With the Carbonmark API, you can:

* Discover available carbon credit inventory
* Retrieve real-time pricing and quotes
* Retire carbon credits programmatically
* Offset fractional quantities starting at 0.001 tCO₂
* Access public retirement records and certificates
* Test your integration for free using sandbox API keys

## Supported networks

The Carbonmark API is multi-network aware and supports carbon credit discovery, pricing, orders, and retirements across Polygon and Base.

<table><thead><tr><th width="147.15625">Network</th><th width="121.73828125">Status</th><th>Supported activity</th></tr></thead><tbody><tr><td>Polygon</td><td>Supported</td><td>Carbon credit discovery, pricing, orders, retirements, and public retirement records</td></tr><tr><td>Base</td><td>Supported</td><td>Klima Protocol carbon class liquidity, pricing, retirements, and public retirement records</td></tr></tbody></table>

Network availability may vary by credit, listing, and liquidity source. API responses include network-aware fields so your integration can determine which chain a price, order, or retirement is associated with.

For transaction links, use the chain-agnostic explorer fields returned by the API instead of network-specific explorer URL fields.

## Common use cases

The API is commonly used to:

* Add carbon retirement to checkout or transaction flows
* Add carbon features to fintech or consumer applications
* Support internal climate, procurement, or reporting workflows
* Integrate carbon retirement into platforms, marketplaces, or enterprise systems

## How it works

The typical integration flow is:

{% stepper %}
{% step %}

### Identify available credits or products

{% endstep %}

{% step %}

### Retrieve pricing in real time

{% endstep %}

{% step %}

### Submit a retirement transaction

{% endstep %}

{% step %}

### Receive confirmation and retirement documentation

{% endstep %}
{% endstepper %}

You do not need to pre-purchase or hold inventory to get starte&#x64;**.**

## Getting started

To complete your first integration:

1. Create an account or sign in with your existing Carbonmark account in the [Developer Dashboard](https://developers.carbonmark.com/login).
2. Generate a free sandbox API key
3. Follow the [Quickstart](/carbonmark-api/quickstart) guide to complete your first test retirement
4. Review the API reference for available endpoints
5. [Contact Carbonmark](https://share-eu1.hsforms.com/1RWJWvyrHT1C_an4cZOHH3gfhhlr) when you are ready for production access and onboarding

{% content-ref url="/pages/SBaid5ydX6Gy87rqSoRm" %}
[Quickstart](/carbonmark-api/quickstart)
{% endcontent-ref %}


# Versioning & Release Process

Learn how Carbonmark API versioning, deprecation, and upgrade timelines work so you can integrate against a stable version.

## Versioning and breaking changes

When consuming our API, **be sure to prefix the API URL with a version number**. For example, the base URL of version 1 would be `v1.api.carbonmark.com`.

Any changes that we make to an API version are guaranteed to be backwards compatible. However, if you omit the version prefix ([`api.carbonmark.com`](http://api.carbonmark.com)) **your application will be exposed to breaking changes** because [api.carbonmark.com](http://api.carbonmark.com) is always routed to the latest major version.

To find the latest version of the API, visit [Quickstart](/carbonmark-api/quickstart)or view the REST reference docs at [api.carbonmark.com](http://api.carbonmark.com) with the version prefix omitted. Likewise you can find the REST reference docs for previous API versions by navigating to the base URL for that version.

## Deprecation policy

When a new version of the Carbonmark API is released, we consider all previous versions to be *deprecated* from that date forward. **We can only guarantee that a deprecated version will stay available to existing users for three months**. After the three month deprecation period the version is considered *retired*.

Retired versions are no longer supported, and are likely to become unstable or be removed completely.

Deprecated versions will be maintained to ensure stability and availability, but they will not receive new features or major improvements.

## Canary versions

Occasionally, we will give a customer early access to a feature or improvements via unreleased “canary” or “staging” URLs.

Please note that these URLs do not include guarantees for stability, compatibility or long-term support and may become unstable within weeks or months. Users are discouraged from sharing these URLs externally, and are encouraged to migrate to the next major version as soon as it is available.

## Upgrading

See individual version pages for release notes and other pertinent upgrade information.


# v20 (latest stable)

### Status

<mark style="color:red;">`Live`</mark> August 17, 2026

### Summary

This API release extends marketplace support to Base alongside Polygon: teams now hold multi-chain wallets, and Base listings, quotes, orders, holdings (including native USDC and carbon credits), and Circle wallets are fully supported. Holdings can also be fetched for multiple wallet addresses in a single request, and Toucan certificates now work on both chains, with native ICR certificates supported.

### Release notes

#### **General**

* Extended Base support: Base marketplace listings, holdings, listing quotes/orders, and Circle wallets
* Teams now support multi-chain wallets (Polygon + Base) instead of a single wallet
* Holdings can be fetched for multiple wallet addresses in one request, including Base native USDC and Base carbon credits
* Toucan certificates work across Polygon and Base; native ICR certificates are supported

#### **Endpoints removed**

* None

#### **Endpoints updated**

**GET** `/holdings/:address`

* Now also returns Base chain holdings (native USDC and carbon credits)
* \[⚠️BREAKING CHANGE] Polygon bridged USDC (`usdc`) holdings are no longer returned — only native USDC per chain

**GET** `/listings`

* Now returns listings from both Polygon and Base marketplaces
* Added `sellerWallets` query parameter (array of addresses)
* \[⚠️DEPRECATED] `sellerWallet` is deprecated; prefer `sellerWallets`

**GET** `/listings/:id`

* Added optional `network` query parameter (`polygon` | `base`; defaults to searching both)

**GET** `/teams/:id`

* Added optional `poll` query parameter (`true` syncs wallets states)

**POST** `/quotes`

* Supports Base marketplace listing `asset_price_source_id` values (e.g. `listing-8453-<listingId>`)

**POST** `/orders`

* Supports Base marketplace listings

**GET** `/retirements/:id/certificate`

* Certificate generation supports Base Toucan and native ICR retirements

#### **Endpoints added**

**GET** `/holdings`

* Get holdings for one or more wallet addresses via the `addresses` query parameter
* Returns Polygon and Base native USDC plus carbon credit holdings

#### **New optional query parameters**

* None

### Migration Path

* **Listings**
  * Replace the `sellerWallet` parameter with `sellerWallets`


# v19 (deprecated)

### Status

<mark style="color:red;">`Deprecated`</mark> August 17, 2026; will become <mark style="color:red;">`Retired`</mark> November 17, 2026

### Summary

This stable release adds Klima Protocol carbon liquidity and prices to the Carbonmark API which is now multi-network aware supporting Polygon and Base.

### Release notes

#### **General**

* Added retire support for Klima Protocol carbon class credits.
* Removed legacy Polygon-specific explorer URL fields from order and retirement payload models, standardizing on chain-agnostic explorer URLs.
* Updated API info limits to be chain-specific instead of a single shared value.

#### **Endpoints removed**

* None

#### **Endpoints updated**

**GET** `/info`

* \[⚠️BREAKING CHANGE] Response shape changed:
  * Removed `MAX_USDC`
  * Added `POLYGON_MAX_USDC`
  * Added `BASE_MAX_USDC`

**POST** `/orders` ; **GET** `/orders` ; **GET** `/orders/:id`

* \[⚠️BREAKING CHANGE] Removed `polygonscan_url` from order response model
* Use `on_chain_explorer_url` instead for both Polygon and Base transactions

**GET** `/retirements` ; **GET** `/retirements/:id`

* \[⚠️BREAKING CHANGE] Removed `polygonscanUrl` from retirement response model
* Use `onChainExplorerUrl` instead for both Polygon and Base transactions

#### **Endpoints added**

* None

#### **New optional query parameters**

* GET `/carbonProjects`
  * assetPriceType
* GET `/carbonProjects/:id`
  * assetPriceType
* `GET /prices`
  * minPriceUSD
  * assetPriceType

The `assetPriceType` filter lets you narrow results to projects with specific pricing sources. Accepted values are `listing` (seller listings on Carbonmark) and `klimaprotocol` (Klima protocol pool prices).

### Migration Path

* **Order response**
  * If you were using `polygonscan_url`, migrate to `on_chain_explorer_url`
* **Retirement response**
  * If you were using `polygonscanUrl`, migrate to `onChainExplorerUrl`
* `/info`
  * Replace reads of `MAX_USDC` with chain-specific handling:
    * `POLYGON_MAX_USDC` for Polygon retirements
    * `BASE_MAX_USDC` for Base retirements


# v18 (retired)

### Status

`Retired` August 11, 2026

### Summary

This stable release streamlines the API around seller listing based orders, unifies retirement IDs across chains, and adds read-only retirement query support on Base. It removes legacy carbon pools and product order types to simplify integration going forward.

### Release notes

#### **General**

* Removed support for carbon pool and product orders - the API now exclusively supports seller listing based orders.
* Updated retirement endpoints to use a unified retirement ID format that works across chains.
* Introduced support for Base network chain read only retirement queries (no support for performing retirements yet).

#### **Endpoints updated**

**GET** `/retirements`

* Now searches both Polygon and Base chain retirements, returning results from both chains.

**GET** `/retirements/:beneficiaryAddress/:retirementIndex`

* \[⚠️ BREAKING CHANGE] This endpoint has been replaced by **GET** `/retirements/:id`. The new endpoint uses a unified retirement ID format (e.g., `84532-0x1ee2facc42147cc6f6cd1fcdca4e3748d2b41b089fafac46515318bbce4f633c-0`) that works across chains. See [#endpoints-added](#endpoints-added "mention") for more info.

**GET** `/retirements/:beneficiaryAddress/:retirementIndex/certificate`

* \[⚠️ BREAKING CHANGE] This endpoint has been replaced by **GET** `/retirements/:id/certificate`. See [#endpoints-added](#endpoints-added "mention") for more info.

**GET** `/retirements/:beneficiaryAddress/:retirementIndex/provenance`

* \[⚠️ BREAKING CHANGE] This endpoint has been replaced by **GET** `/retirements/:id/provenance`. See [#endpoints-added](#endpoints-added "mention") for more info.

**GET** `/prices`

* \[⚠️ BREAKING CHANGE] Removed the following query parameters:
  * `productIds` - Products are no longer supported
  * `hasAssetPriceType` - Only listing prices are now returned
  * `isDefaultCredit` - Carbon pools are no longer supported
* The endpoint now exclusively returns prices from listing created by sellers.

**POST** `/quotes`

* \[⚠️ BREAKING CHANGE] Simplified request body - now only accepts `asset_price_source_id` and `quantity_tonnes` .
* Removed deprecated parameters: `listing_id`, `pool`, `credit_token_address` .
* Removed support for carbon pool and product quotes - only listing quotes are supported.

**POST** `/orders`

* \[⚠️ BREAKING CHANGE] Now only supports listing orders. Carbon pool and product orders are no longer supported.
* Requests with non-listing `asset_price_source_id` values will return a 400 error.

**GET** `/carbonProjects`

* Updated to return only listing prices.

#### **Endpoints added**

**GET** `/retirements/:id`

* Get retirement details using a unified retirement ID format that works across Polygon and Base chains.
* The retirement ID format is: `{chainId}-{transactionHash}-{retirementIndex}` (e.g., `84532-0x1ee2facc42147cc6f6cd1fcdca4e3748d2b41b089fafac46515318bbce4f633c-0`).
* This replaces the previous `/retirements/:beneficiaryAddress/:retirementIndex` endpoint.

**GET** `/retirements/:id/certificate`

* Get retirement certificate using a unified retirement ID.
* This replaces the previous `/retirements/:beneficiaryAddress/:retirementIndex/certificate` endpoint.

**GET** `/retirements/:id/provenance`

* Get retirement provenance records using a unified retirement ID.
* This replaces the previous `/retirements/:beneficiaryAddress/:retirementIndex/provenance` endpoint.

#### Endpoints removed

**GET** `/products`

* \[⚠️ BREAKING CHANGE] This endpoint has been removed. Products are no longer supported.

**GET** `/products/:id`

* \[⚠️ BREAKING CHANGE] This endpoint has been removed. Products are no longer supported.

**GET** `/products/:id/stats`

* \[⚠️ BREAKING CHANGE] This endpoint has been removed. Products are no longer supported.

### Migration Path

* **Products endpoints**
  * If you were using **GET** `/products`, **GET** `/products/:id`, or **GET** `/products/:id/stats`, these endpoints have been removed. Products are no longer supported as a retirement source. You should use listing-based orders instead.
* **Retirement endpoints**
  * If you were using **GET** `/retirements/:beneficiaryAddress/:retirementIndex`, you should now use **GET** `/retirements/:id` .
  * If you were using **GET** `/retirements/:beneficiaryAddress/:retirementIndex/certificate`, you should now use **GET** `/retirements/:id/certificate` .
  * If you were using **GET** `/retirements/:beneficiaryAddress/:retirementIndex/provenance`, you should now use **GET** `/retirements/:id/provenance` .
  * The retirement ID can be obtained from the **GET** `/retirements` endpoint, which returns retirements with their IDs.
* **Prices endpoint**
  * If you were filtering by `productIds`, `hasAssetPriceType`, or `isDefaultCredit`, these parameters have been removed. The endpoint now only returns listing prices.
* **Quotes endpoint**
  * If you were using deprecated parameters like `listing_id`, `pool`, or `credit_token_address`, you should now use `asset_price_source_id` .
  * Carbon pool and product quotes are no longer supported - only listing quotes are available.


# v17 (retired)

### Status

`Retired` March 8, 2026

No longer supported.

### Summary

v17 is primarily focused on improving the stability and performance of order processing. It includes a few breaking changes from v16 (highlighted below).

### Release notes

#### **General**

* Removed <mark style="color:red;">`network`</mark> query parameter from the following endpoints. Default network is <mark style="color:red;">`Polygon`</mark>.
  * **GET** `/activities`
  * **GET** `/carbonProjects`
  * **GET** `/carbonProjects/:id`
  * **GET** `/categories`
  * **GET** `/countries`
  * **GET** `/listings/:id`
  * **GET** `/listings`
  * **GET** `/purchases/:id`
  * **GET** `/purchases`
  * **GET** `/prices`
  * **GET** `/retirements`
  * **GET** `/retirements/:id`
  * **GET** `/retirements/:id/provenance`
  * **GET** `/vintages`

#### **Endpoints updated**

**GET** `/carbonProjects`

* \[⚠️BREAKING CHANGE] Response entries are now paginated
* Added a **block\_long\_description** field that contains a project description in **PortableText** format

**GET** `/carbonProjects/:id`

* Added a **block\_long\_description** field that contains a project description in **PortableText** format

#### **Endpoints added**

**GET** `/assessors`

* Returns the list of known project assessors (related to project verification and validation)

**GET** `/developers`

* Returns the list of known project developers

**GET** `/retirements/:beneficiaryAddress/:retirementIndex`

* Get retirement details for a completed retirement order. This replaces the endpoint that was present in previous versions (see [Endpoints removed](#endpoints-removed) below)

**GET** `/retirements/:beneficiaryAddress/:retirementIndex/provenance`

* Get the credit provenance history leading up to a completed retirement. This replaces the `/provenance` endpoint that was present in previous versions (see [Endpoints removed](#endpoints-removed) below)

#### **Endpoints removed**

**GET** `/retirements/:hash`

* \[⚠️BREAKING CHANGE] This endpoint was removed because a transaction hash may now include more than one retirement. It has been replaced by `/retirements/:beneficiaryAddress/:retirementIndex`. See [Endpoints added ](#endpoints-added)for more info. See [migration path](#migration-path) for alternatives.

**GET** `/retirements/:hash/provenance`

* \[⚠️BREAKING CHANGE] This endpoint was removed because a transaction hash may now include more than one retirement. It has been replaced by `/retirements/:beneficiaryAddress/:retirementIndex`. See [Endpoints added ](#endpoints-added)for more info. See [migration path](#migration-path) for alternatives.

### Migration Path

* **Carbon Projects endpoint**
  * The [`/carbonProjects`](https://v17.api.carbonmark.com/#/paths/carbonProjects/get) endpoint is now paginated. Your UI or system may need to be refactored to consume pages, rather than a complete list of projects. The project data is now provided in the **items** attribute of the response.
* **Retirement endpoints**
  * If you were using the **GET** `/retirements/:hash` and **GET** `/retirements/:hash/provenance` you should now use the new endpoints:
    * **GET** `/retirements/:beneficiaryAddress/:retirementIndex`
    * **GET** `/retirements/:beneficiaryAddress/:retirementIndex/provenance`
  * If you only have the transaction hash you can still use the hash to filter on the **GET** `/retirements` endpoint, which returns a list.
    * Example using hash to filter:\
      <https://v17.api.carbonmark.com/retirements?hash=0x7b63b79d25c76b6f179360ebd5f9f3c2435d3fdf2dda7e1d3ab43cf4f90e4a04>


# v16 (retired)

### Status

`Retired` September 4th, 2025

No longer supported.

### Summary

The v16 release of the Carbonmark API introduces enhanced query parameters, streamlined models, and improved filtering capabilities across key endpoints. While some attributes and endpoints have been deprecated or removed, new features like wallet-specific queries and retirement certificate generation simplify and expand functionality. Migration guidance is provided to ensure a smooth transition.

### Release notes

#### **Endpoints updated**

* **GET** `/carbonProjects` endpoint:
  * Added a **registry** and **isDefaultProject** query parameters.
  * Added a **satelliteImage** attribute.
* **GET** `/holdings` endpoint:
  * Added a **minAmountTonnes** query parameter.
  * The **amount** attribute of the Holding model is now returned as a number.
* **GET** `/listings` endpoint:
  * Added a **sellerWallet**, **projectIds, tokenIdentifier, active** and **ids** query parameters to improve the filtering capabilities.
  * Removed the **batches** and **batchPrices** attributes.
  * Deprecated the **symbol**, **tokenId** and **tokenStandard** attributes in favour of a **token** object.
  * Added a **creditId** attribute.
* **GET** `/prices` endpoint:
  * Added a **isDefaultCredit** query parameter.
* **GET** `/retirements` endpoint:
  * Removed the **retireeProfile** attribute.
  * Added a **asyncRetirement** attribute.
* **GET** `/activities` endpoint:
  * Added a **walletAddress** query parameter.
* **GET** `/teams` endpoint:
  * Added a **wallet\_address** and **beneficiary\_address** query parameters.
  * The **wallet** attribute is replaced by a **wallets** attribute which contains an array of the team’s wallet ids.
* **Token model**
  * The **decimals** and **tokenStandard** attributes have been added to the model.

#### **Endpoints added**

* **GET** `/holdings/:address`
  * Returns the holdings of the wallet at the given address.
* **GET** `/retirements/:beneficiaryAddress/:retirementIndex/certificate`
  * Generates a certificate for the given retirement.
* **GET** `/retirements/:beneficiaryAddress/:retirementIndex/status`
  * Returns the indexing information of the given retirement.
* **GET** `/users/:handle`
  * Returns of the wallet at the given address.
* **PUT** `/teams/:id/:pin`
  * Creates a challenge to create or update a team’s PIN.
* **POST** `/wallets`
  * Creates a challenge to create a team’s wallet.
* **POST** `/wallets/:uuid/:transactions`
  * Creates a challenge to perform a transaction with the given wallet.
* **GET** `/wallets/:uuid`
  * Returns information for the given wallet.
* **GET** `/wallets/:uuid/:allowances`
  * Returns token allowances of the given wallet.
* **GET** `/wallets/:uuid/:transactions`
  * Returns transactions performed with the given wallet.

#### **Endpoints removed**

* **GET** `/users/:walletOrHandle`
* **POST** `/users`
* **PUT** `/users/:wallet`

### Migration Path

* **Retirement API**
  * No breaking changes were made to the retirement API (`/quotes` and `/orders` endpoints).
* **Web migration**
  * For entities that created a profile on Carbonmark, you could get information about them by using the `GET /users/:walletOrHandle` endpoint. In this new version of the API you should use the `GET /teams` endpoints.
    * Example: <https://v16.api.carbonmark.com/teams?wallet_address=0xf3de051d4183b1a9a1258c9815cedb0ac725a65f>
* **Other breaking changes**
  * If you were using the `GET /holdings` endpoint make sure that you take into account that the amount attribute is now a number.
  * If you were using the **retireeProfile** information from the Retirement model; you would need to perform an additional query to the `GET /teams` endpoint (see Web migration).
  * If you were using the **batches and batchesPrices** attributes of the Listing model, [please contact us](https://share-eu1.hsforms.com/1RWJWvyrHT1C_an4cZOHH3gfhhlr).

### References

* The Token model: <https://v16.api.carbonmark.com/#/schemas/Token>


# v15 (retired)

### Status

`Retired` April 29th, 2025

No longer supported.

### Release notes

* in the `/users` endpoint the token object attributes have been aligned:
  * address is added
  * decimals is removed
* in the `/prices` endpoint the carbonCredit attribute that existed in the listing and carbon\_pool objects have been split into a token and a creditId objects. in addition a symbol attribute was added to the token object (for consistency with the token object in the `/users` endpoint)
* in the `/prices` the product object now contains the corresponding token object
* the `/prices` endpoint now expects an arrays instead of a string for the vintage arguments
* in the `/retirements` and `/retirements/:id` endpoints the credit attribute has been split into a token and a creditId objects. in addition a symbol attribute was added to the token object (for consistency with the token object in the `/users` endpoint)
* the id, asset\_id, erc1155\_token\_id, listing\_id, pool\_name attribute are removed from the `/quotes` endpoints, they are replaced by the asset\_price\_source\_id attribute
* the id attribute is removed from the `/orders` endpoints
* the `/carbonProject` endpoint now expects arrays instead of strings for the following arguments: country, category, vintage, sdg

### References

* The Token model: <https://v15.api.carbonmark.com/#/schemas/Token>


# Quickstart

Complete your first Carbonmark API integration by generating a sandbox key and submitting a test retirement.

## Latest stable version

[https://v20.api.carbonmark.com](https://v20.api.carbonmark.com/)

## Use your preferred API client

The examples in these docs use `curl`, but you can follow the same workflow in any API client, including Postman, Insomnia, Bruno, or your own HTTP tooling.

If your client supports OpenAPI imports, you can import the Carbonmark OpenAPI spec and authenticate using your API key as a Bearer token.

The workflow remains the same:

1. Retrieve a listing price source from `/prices`
2. Create a quote with `/quotes`
3. Create an order with `/orders`
4. Confirm completion by querying `/orders` with the `quote_uuid`

## Getting started

1. Create an account or sign in with your existing Carbonmark account in the [Developer Dashboard](https://developers.carbonmark.com/login).
2. Once logged in, visit the [Keys](https://developers.carbonmark.com/dashboard/keys) page to generate a free sandbox API key.
3. Review the [API reference](https://api.carbonmark.com/) and guides for available endpoints and workflows.
4. [Contact Carbonmark](https://share-eu1.hsforms.com/1RWJWvyrHT1C_an4cZOHH3gfhhlr) when you are ready for production access and onboarding.

{% content-ref url="/pages/rOXfIFwLRhsqxJicKxjb" %}
[Versioning & Release Process](/carbonmark-api/versioning-and-release-process)
{% endcontent-ref %}

{% content-ref url="/pages/GhffJyxYkU82lU0er6G3" %}
[Retire Carbon](/carbonmark-api/retire-carbon)
{% endcontent-ref %}

## Explore projects

You can use the Carbonmark REST API to view assets, prices, supply, and other variables.

{% content-ref url="/pages/eT5gmNLlKHw14n7RvCFD" %}
[Explore Carbon Projects](/carbonmark-api/explore-carbon-projects)
{% endcontent-ref %}


# Explore Carbon Projects

Use the Carbonmark API to find carbon projects by country, vintage, methodology, or project details.

In this section you will learn to:

* Find carbon projects by country
* Find carbon projects by vintage
* Find carbon projects by methodology
* Find carbon projects by project name or description


# Find carbon projects by country

Use the Carbonmark API to retrieve available country values and return carbon projects that match a selected country.

This page shows the basic workflow. For full parameter definitions and response schemas, see the [API reference](https://api.carbonmark.com/) for each endpoint.

## Step 1: Retrieve available countries

Call the [`/countries`](https://api.carbonmark.com/#/paths/countries/get) endpoint to return the list of valid country values you can use when filtering carbon projects.

```bash
curl --request GET \
  --url https://api.carbonmark.com/countries \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```json
[
  { "id": "Brazil" },
  { "id": "Bulgaria" },
  { "id": "China" },
  { "id": "Congo" },
  { "id": "Ecuador" },
  { "id": "India" }
]
```

## Step 2: Filter projects by country

Once you have a valid country value, pass it to the [`/carbonProjects`](https://api.carbonmark.com/#/paths/carbonProjects/get) endpoint.

You can also combine `country` with other supported filters such as:

* `category`
* `vintage`
* project name
* project description
* assetPriceType

The `assetPriceType` filter lets you narrow results to projects with specific pricing sources. Accepted values are `listing` (seller listings on Carbonmark) and `klimaprotocol` (Klima protocol pool prices).

The example below returns renewable energy projects in India with a 2012 vintage.

```bash
curl -G https://api.carbonmark.com/carbonProjects \
  --data-urlencode "country=India" \
  --data-urlencode "category=Renewable Energy" \
  --data-urlencode "vintage=2012" \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```json
[
  {
    "key": "VCS-498",
    "projectID": "498",
    "name": "Grid-Connected Wind Electricity Generation Project In Tamil Nadu, India",
    "country": "India",
    "region": "Asia",
    "registry": "VCS",
    "methodologies": [
      {
        "id": "AMS-ID",
        "category": "Renewable Energy",
        "name": "Grid connected renewable electricity generation"
      }
    ],
    "vintages": ["2012"],
    "price": "0.91",
    "hasSupply": true,
    "stats": {
      "totalBridged": 26236,
      "totalRetired": 300.0021,
      "totalSupply": 25922.899
    },
    "url": "https://registry.verra.org/app/projectDetail/VCS/498"
  },
  {
    "key": "VCS-1578",
    "projectID": "1578",
    "name": "15 MW Solar Photovoltaic Power Project At Gujarat",
    "country": "India",
    "region": "Asia",
    "registry": "VCS",
    "methodologies": [
      {
        "id": "AMS-ID",
        "category": "Renewable Energy",
        "name": "Grid connected renewable electricity generation"
      }
    ],
    "vintages": ["2012"],
    "price": "0.91",
    "hasSupply": true,
    "stats": {
      "totalBridged": 1936,
      "totalRetired": 1300,
      "totalSupply": 636
    },
    "url": "https://registry.verra.org/app/projectDetail/VCS/1578"
  }
]
```

For many workflows, the `key` and `projectID` fields are enough to display results or request full project details.

## Step 3: Retrieve a single project by key

If you need the full details for a specific project, call [`/carbonProjects/{key}`](https://api.carbonmark.com/#/paths/carbonProjects-id/get) using the project `key` returned in the search response. You can also filter the returned asset prices by type using the `assetPriceType` parameter.

```bash
curl --request GET \
  --url https://api.carbonmark.com/carbonProjects/VCS-274 \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```json
{
  "key": "VCS-274",
  "projectID": "274",
  "name": "Hanuman Ganga Hydro (4.95 MW) Plant At Uttarakhand",
  "country": "India",
  "region": "Asia",
  "registry": "VCS",
  "methodologies": [
    {
      "id": "AMS-ID",
      "category": "Renewable Energy",
      "name": "Grid connected renewable electricity generation"
    }
  ],
  "vintages": [],
  "price": "0",
  "hasSupply": false,
  "stats": {
    "totalBridged": 39492,
    "totalRetired": 2.0021,
    "totalSupply": 0
  },
  "sustainableDevelopmentGoals": ["7", "8", "9", "13"],
  "url": "https://registry.verra.org/app/projectDetail/VCS/274"
}
```

## Notes

* Use `/countries` first to avoid passing unsupported country values.
* Use `/carbonProjects` when you want a filtered list of matching projects.
* Use `/carbonProjects/{key}` when you need full details for a specific project.
* Refer to the API reference for the complete list of supported parameters and response fields.


# Find carbon projects by vintage

Use the Carbonmark API to retrieve available vintages and return carbon projects that match a selected vintage.

Use the Carbonmark API to retrieve available vintages and return carbon projects that match a selected vintage.

This page shows the basic workflow. For full parameter definitions and response schemas, see the [API reference](https://api.carbonmark.com/) for each endpoint.

## Step 1: Retrieve available vintages

Call the [`/vintages`](https://api.carbonmark.com/#/paths/vintages/get) endpoint to return the list of valid vintage values you can use when filtering carbon projects.

```bash
curl --request GET \
  --url https://api.carbonmark.com/vintages \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
[
  "2006",
  "2007",
  "2008",
  "2009",
  "2010",
  "2011",
  "2012",
  "2013",
  "2014",
  "2015",
  "2016",
  "2017",
  "2018",
  "2019",
  "2020"
]
```

## Step 2: Filter projects by vintage

Once you have a valid vintage value, pass it to the [`/carbonProjects`](https://api.carbonmark.com/#/paths/carbonProjects/get) endpoint.

You can also combine `vintage` with other supported filters such as:

* country
* category
* project name
* project description
* assetPriceType

The `assetPriceType` filter lets you narrow results to projects with specific pricing sources. Accepted values are `listing` (seller listings on Carbonmark) and `klimaprotocol` (Klima protocol pool prices).

The example below returns projects with a 2020 vintage.

```bash
curl -G https://api.carbonmark.com/carbonProjects \
  --data-urlencode "vintage=2020" \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
[
  {
    "key": "VCS-1547",
    "projectID": "1547",
    "name": "5MW Biomass Based Cogeneration Project At Sainsons",
    "country": "India",
    "region": "Asia",
    "registry": "VCS",
    "methodologies": [
      {
        "id": "ACM0006",
        "category": "Other",
        "name": "Electricity and heat generation from biomass"
      }
    ],
    "vintages": ["2020"],
    "price": "1.4",
    "hasSupply": true,
    "stats": {
      "totalBridged": 3100,
      "totalRetired": 667.831,
      "totalSupply": 0
    },
    "url": "https://registry.verra.org/app/projectDetail/VCS/1547"
  },
  {
    "key": "ICR-349",
    "projectID": "349",
    "name": "Forestal Río Aquidabán",
    "country": "Paraguay",
    "region": "South America",
    "registry": "ICR",
    "methodologies": [
      {
        "id": "AR-ACM0003",
        "category": "Forestry",
        "name": "Afforestation and reforestation of lands except wetlands"
      }
    ],
    "vintages": ["2020"],
    "price": "22.834",
    "hasSupply": true,
    "stats": {
      "totalBridged": 255379,
      "totalRetired": 961.886,
      "totalSupply": 67992.856
    },
    "url": "https://www.carbonregistry.com/projects/forestal-rio-aquidaban-349?tab=overview"
  }
]
```

For many workflows, the `key` and `projectID` fields are enough to display results or request full project details.

## Step 3: Retrieve a single project by key

If you need the full details for a specific project, call [`/carbonProjects/{key}`](https://api.carbonmark.com/#/paths/carbonProjects-id/get) using the project `key` returned in the search response. You can also filter the returned asset prices by type using the `assetPriceType` parameter.

```bash
curl --request GET \
  --url https://api.carbonmark.com/carbonProjects/VCS-1418 \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
{
  "key": "VCS-1418",
  "projectID": "1418",
  "name": "Renewable Energy Project By LNB Group",
  "country": "India",
  "region": "Asia",
  "registry": "VCS",
  "methodologies": [
    {
      "id": "ACM0002",
      "category": "Renewable Energy",
      "name": "Grid-connected electricity generation from renewable sources"
    }
  ],
  "vintages": [],
  "price": "0",
  "hasSupply": false,
  "stats": {
    "totalBridged": 2,
    "totalRetired": 1.1895,
    "totalSupply": 0
  },
  "sustainableDevelopmentGoals": ["7", "8", "9", "13"],
  "url": "https://registry.verra.org/app/projectDetail/VCS/1418"
}
```

## Notes

* Use `/vintages` first to avoid passing unsupported vintage values.
* Use `/carbonProjects` when you want a filtered list of matching projects.
* Use `/carbonProjects/{key}` when you need full details for a specific project.
* Refer to the API reference for the complete list of supported parameters and response fields.


# Find carbon projects by methodology category

Use the Carbonmark API to retrieve available methodology categories and return carbon projects that match a selected category.

Use the Carbonmark API to retrieve available methodology categories and return carbon projects that match a selected category.

This page shows the basic workflow. For full parameter definitions and response schemas, see the [API reference](https://api.carbonmark.com/) for each endpoint.

## Step 1: Retrieve available methodology categories

Call the [`/categories`](https://api.carbonmark.com/#/paths/categories/get) endpoint to return the list of valid methodology category values you can use when filtering carbon projects.

```bash
curl --request GET \
  --url https://api.carbonmark.com/categories \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
[
  { "id": "Agriculture" },
  { "id": "Biochar" },
  { "id": "Blue Carbon" },
  { "id": "Energy Efficiency" },
  { "id": "Forestry" },
  { "id": "Industrial Processing" },
  { "id": "Other" },
  { "id": "Renewable Energy" },
  { "id": "Waste Disposal" }
]
```

## Step 2: Filter projects by methodology category

Once you have a valid methodology category value, pass it to the [`/carbonProjects`](https://api.carbonmark.com/#/paths/carbonProjects/get) endpoint.

You can also combine `category` with other supported filters such as:

* country
* vintage
* project name
* project description
* assetPriceType

The `assetPriceType` filter lets you narrow results to projects with specific pricing sources. Accepted values are `listing` (seller listings on Carbonmark) and `klimaprotocol` (Klima protocol pool prices).

The example below returns projects in the `Forestry` methodology category from Indonesia.

```bash
curl -G https://api.carbonmark.com/carbonProjects \
  --data-urlencode "country=Indonesia" \
  --data-urlencode "category=Forestry" \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
[
  {
    "key": "VCS-674",
    "projectID": "674",
    "name": "Rimba Raya Biodiversity Reserve Project",
    "country": "Indonesia",
    "region": "Oceania",
    "registry": "VCS",
    "methodologies": [
      {
        "id": "VM0004",
        "category": "Forestry",
        "name": "Methodology for Conservation Projects that Avoid Planned Land Use Conversion in Peat Swamp Forests"
      }
    ],
    "vintages": [],
    "price": "0",
    "hasSupply": false,
    "stats": {
      "totalBridged": 229030,
      "totalRetired": 208614.55942326694,
      "totalSupply": 0
    },
    "url": "https://registry.verra.org/app/projectDetail/VCS/674"
  },
  {
    "key": "VCS-1477",
    "projectID": "1477",
    "name": "Katingan Peatland Restoration And Conservation Project",
    "country": "Indonesia",
    "region": "Asia",
    "registry": "VCS",
    "methodologies": [
      {
        "id": "VM0007",
        "category": "Forestry",
        "name": "REDD+ Methodology Framework (REDD+MF)"
      }
    ],
    "vintages": [],
    "price": "0",
    "hasSupply": false,
    "stats": {
      "totalBridged": 0,
      "totalRetired": 0,
      "totalSupply": 0
    },
    "url": "https://registry.verra.org/app/projectDetail/VCS/1477"
  }
]
```

For many workflows, the `key` and `projectID` fields are enough to display results or request full project details.

## Step 3: Retrieve a single project by key

If you need the full details for a specific project, call [`/carbonProjects/{key}`](https://api.carbonmark.com/#/paths/carbonProjects-id/get) using the project `key` returned in the search response. You can also filter the returned asset prices by type using the `assetPriceType` parameter.

```bash
curl --request GET \
  --url https://api.carbonmark.com/carbonProjects/VCS-674 \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
{
  "key": "VCS-674",
  "projectID": "674",
  "name": "Rimba Raya Biodiversity Reserve Project",
  "country": "Indonesia",
  "region": "Oceania",
  "registry": "VCS",
  "methodologies": [
    {
      "id": "VM0004",
      "category": "Forestry",
      "name": "Methodology for Conservation Projects that Avoid Planned Land Use Conversion in Peat Swamp Forests"
    }
  ],
  "vintages": [],
  "price": "0",
  "hasSupply": false,
  "stats": {
    "totalBridged": 229030,
    "totalRetired": 208614.55942326694,
    "totalSupply": 0
  },
  "sustainableDevelopmentGoals": [
    "1", "2", "3", "4", "5", "6", "7", "8",
    "9", "10", "11", "12", "13", "14", "15", "16", "17"
  ],
  "url": "https://registry.verra.org/app/projectDetail/VCS/674"
}
```

## Notes

* Use `/categories` first to avoid passing unsupported methodology category values.
* Use `/carbonProjects` when you want a filtered list of matching projects.
* Use `/carbonProjects/{key}` when you need full details for a specific project.
* Refer to the API reference for the complete list of supported parameters and response fields.


# Find carbon projects by project name or description

Use the Carbonmark API to search carbon projects by project name or description using a text query.

Use the Carbonmark API to search carbon projects by project name or description using a text query.

This page shows the basic workflow. For full parameter definitions and response schemas, see the [API reference](https://api.carbonmark.com/) for each endpoint.

## Step 1: Search projects using a text query

Call the [`/carbonProjects`](https://api.carbonmark.com/#/paths/carbonProjects/get) endpoint with the `search` query parameter to return projects whose name or description matches a text string.

The `assetPriceType` filter lets you narrow results to projects with specific pricing sources. Accepted values are `listing` (seller listings on Carbonmark) and `klimaprotocol` (Klima protocol pool prices).

The example below searches for projects containing the string `REDD+`.

```bash
curl -G https://api.carbonmark.com/carbonProjects \
  --data-urlencode "search=REDD+" \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
[
  {
    "key": "VCS-844",
    "projectID": "844",
    "name": "Madre De Dios Amazon REDD+ Project",
    "country": "Peru",
    "region": "Latin America",
    "registry": "VCS",
    "methodologies": [
      {
        "id": "VM0007",
        "category": "Forestry",
        "name": "REDD+ Methodology Framework (REDD+MF)"
      }
    ],
    "vintages": [],
    "price": "0",
    "hasSupply": false,
    "stats": {
      "totalBridged": 0,
      "totalRetired": 0,
      "totalSupply": 0
    },
    "url": "https://registry.verra.org/app/projectDetail/VCS/844"
  },
  {
    "key": "VCS-1811",
    "projectID": "1811",
    "name": "Jari/Pará REDD+ Project",
    "country": "Brazil",
    "region": "Latin America",
    "registry": "VCS",
    "methodologies": [
      {
        "id": "VM0015",
        "category": "Forestry",
        "name": "Methodology for Avoided Unplanned Deforestation"
      }
    ],
    "vintages": [],
    "price": "0",
    "hasSupply": false,
    "stats": {
      "totalBridged": 0,
      "totalRetired": 0,
      "totalSupply": 0
    },
    "url": "https://registry.verra.org/app/projectDetail/VCS/1811"
  }
]
```

For many list and search workflows, `key`, `projectID`, `name`, `registry`, `country`, `price`, and `hasSupply` are the most useful fields to start with.

## Step 2: Retrieve a single project by key

If you need the full details for a specific project, call [`/carbonProjects/{key}`](https://api.carbonmark.com/#/paths/carbonProjects-id/get) using the project `key` returned in the search response. You can also filter the returned asset prices by type using the `assetPriceType` parameter.

```bash
curl --request GET \
  --url https://api.carbonmark.com/carbonProjects/VCS-1052 \
  --header 'Accept: application/json'
```

Example response (trimmed for readability):

```bash
{
  "key": "VCS-1052",
  "projectID": "1052",
  "name": "North Pikounda REDD+",
  "country": "Congo, The Democratic Republic of the",
  "region": "Africa",
  "registry": "VCS",
  "methodologies": [
    {
      "id": "VM0011",
      "category": "Forestry",
      "name": "Methodology for Calculating GHG Benefits from Preventing Planned Degradation"
    }
  ],
  "vintages": ["2012"],
  "price": "1.82",
  "hasSupply": true,
  "stats": {
    "totalBridged": 55532,
    "totalRetired": 46621.89476754726,
    "totalSupply": 452.999
  },
  "sustainableDevelopmentGoals": ["13", "15"],
  "url": "https://registry.verra.org/app/projectDetail/VCS/1052"
}
```

## Notes

* Use the `search` query parameter to match text in project names and descriptions.
* Use `/carbonProjects` when you want a filtered list of matching projects.
* Use `/carbonProjects/{key}` when you need full details for a specific project.
* Refer to the API reference for the complete list of supported parameters and response fields.


# Retire Carbon

Use the Carbonmark API to create retirement quotes, submit retirement orders, and confirm completion with a retirement receipt.

Use the Carbonmark API to create retirement quotes, submit retirement orders, and confirm completion with a retirement receipt.

This page shows the basic retirement workflow. For full parameter definitions and response schemas, see the [API reference](https://api.carbonmark.com/) for each endpoint.

In the examples below, we use a versioned API base URL such as `https://v1.api.carbonmark.com`. Omitting the version prefix exposes your integration to breaking changes. Use the [latest stable version](/carbonmark-api/versioning-and-release-process) when building your integration.

## Before you begin

You will need:

* a Carbonmark account
* a sandbox or production API key
* a project or product with a visible price
* an `asset_price_source_id` from the `/prices` endpoint

Create a sandbox key in the [Developer Dashboard](https://developers.carbonmark.com/) for testing. Production access requires onboarding. Keep your API key secure and do not expose it in client-side code or commit it to your repository.

## Step 1: Create an API key

Create an account or sign in to the Developer Dashboard.

Once signed in, go to the **Keys** page and generate an API key.

* Sandbox keys are free and can be used for testing
* Production keys require onboarding
* Keys are shown only once, so copy and store them securely

## Step 2: Choose a project to retire

Use the Carbonmark marketplace or the API to find a project with a visible listing price.

For project-based retirements, use [`/carbonProjects`](https://api.carbonmark.com/#/paths/carbonProjects/get) and note the project `key`.

In this example, we use project `ICR-112`.

## Step 3: Retrieve pricing and identify an asset price source

Before creating a quote, call `/prices` to retrieve seller listing price sources for the project you want to retire. The `assetPriceType` filter lets you narrow results to projects with specific pricing sources. Accepted values are `listing` (seller listings on Carbonmark) and `klimaprotocol` (Klima protocol pool prices).

Use the `sourceId` value from the `/prices` response as the `asset_price_source_id` in the quote request.

Example request:

```bash
curl -G https://v1.api.carbonmark.com/prices \
  --data-urlencode "projectIds=ICR-112" \
  --header "Accept: application/json"
```

Example response (trimmed for readability):

```bash
[
  {
    "sourceId": "listing-137-0xe5a7178a0c44cac6f5a00b429b3de99016194b1494e75f4ef849a81faad41504",
    "type": "listing",
    "purchasePrice": 3.08,
    "baseUnitPrice": 2.2,
    "supply": 0.872,
    "liquidSupply": 0.872,
    "minFillAmount": 0.001,
    "listing": {
      "id": "0xe5a7178a0c44cac6f5a00b429b3de99016194b1494e75f4ef849a81faad41504",
      "creditId": {
        "vintage": 2019,
        "projectId": "ICR-112"
      },
      "token": {
        "id": "137-erc1155-0xd016b2acece65612b93cc9aee763bda0c2b0e4c0-1",
        "address": "0xd016b2acece65612b93cc9aee763bda0c2b0e4c0",
        "decimals": 18,
        "tokenStandard": "erc1155",
        "name": "ICR-112-2019",
        "isExAnte": false,
        "symbol": "ICR-112-2019",
        "tokenId": 1
      },
      "sellerId": "0x6e069bd92d6d52ee6ed79753274840469dd8ec62",
      "expiresAfter": 1894088958
    }
  },
  {
    "sourceId": "listing-137-0x79a431e9ea34117e57e2cc4cc483f6954ac295e8de540faa320dc8697f4a5a8b",
    "type": "listing",
    "purchasePrice": 3.78,
    "baseUnitPrice": 2.7,
    "supply": 19.361,
    "liquidSupply": 19.361,
    "minFillAmount": 0.001,
    "listing": {
      "id": "0x79a431e9ea34117e57e2cc4cc483f6954ac295e8de540faa320dc8697f4a5a8b",
      "creditId": {
        "vintage": 2019,
        "projectId": "ICR-112"
      },
      "token": {
        "id": "137-erc1155-0xd016b2acece65612b93cc9aee763bda0c2b0e4c0-1",
        "address": "0xd016b2acece65612b93cc9aee763bda0c2b0e4c0",
        "decimals": 18,
        "tokenStandard": "erc1155",
        "name": "ICR-112-2019",
        "isExAnte": false,
        "symbol": "ICR-112-2019",
        "tokenId": 1
      },
      "sellerId": "0x6e069bd92d6d52ee6ed79753274840469dd8ec62",
      "expiresAfter": 1897738887
    }
  }
]
```

For this example flow, we use the second listing because it has enough supply to support a 1 tonne retirement.

## Step 4: Create a retirement quote

Create a quote using the [`/quotes`](https://api.carbonmark.com/#/paths/quotes/post) endpoint.

Required fields:

* `asset_price_source_id`
* `quantity_tonnes`

Example request:

```bash
curl --request POST \
  --url https://v1.api.carbonmark.com/quotes \
  --header "Accept: application/json" \
  --header "Authorization: Bearer YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "asset_price_source_id": "listing-137-0x79a431e9ea34117e57e2cc4cc483f6954ac295e8de540faa320dc8697f4a5a8b",
    "quantity_tonnes": 1
  }'
```

Example response (trimmed for readability):

```bash
{
  "asset_price_source_id": "listing-137-0x79a431e9ea34117e57e2cc4cc483f6954ac295e8de540faa320dc8697f4a5a8b",
  "uuid": "07495f6c-8b02-465d-8fdd-4bcc2d2047e9",
  "quantity_tonnes": 1,
  "cost_usdc": 3.78,
  "consumed": 0
}
```

Save the `uuid` from the quote response. You will need it to create the order.

## Step 5: Create a retirement order

Create an order using the [`/orders`](https://api.carbonmark.com/#/paths/orders/get) endpoint.

Required fields:

* `quote_uuid`
* `beneficiary_name`
* `retirement_message`

Some registries may require additional fields. For example, certain Puro.earth retirements require `consumption_metadata`.

Example request:

```bash
curl --request POST \
  --url https://v1.api.carbonmark.com/orders \
  --header "Accept: application/json" \
  --header "Authorization: Bearer YOUR_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "quote_uuid": "07495f6c-8b02-465d-8fdd-4bcc2d2047e9",
    "beneficiary_name": "Mother Nature",
    "beneficiary_address": "0x6fb0e62afB345951997bd5149a313df9fcB72C2d",
    "retirement_message": "Retirement of 1 tonne from ICR-112"
  }'
```

Example response (trimmed for readability):

```bash
{
  "status": "SUBMITTED",
  "transaction_hash": null,
  "polygonscan_url": null,
  "view_retirement_url": null,
  "quote": {
    "uuid": "07495f6c-8b02-465d-8fdd-4bcc2d2047e9",
    "quantity_tonnes": 1,
    "cost_usdc": 3.78
  },
  "consumption_metadata": {},
  "registry_specific_data": {}
}
```

A successful order response with `status: "SUBMITTED"` means the retirement request has been accepted and is being processed.

## Step 6: Confirm the retirement is complete

To confirm completion, query `/orders` using the `quote_uuid`.

Example request:

```bash
curl -G https://v1.api.carbonmark.com/orders \
  --data-urlencode "quote_uuid=07495f6c-8b02-465d-8fdd-4bcc2d2047e9" \
  --header "Accept: application/json" \
  --header "Authorization: Bearer YOUR_API_KEY"
```

Example response (trimmed for readability)

```bash
{
  "status": "COMPLETED",
  "transaction_hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  "polygonscan_url": "https://polygonscan.com/tx/0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  "view_retirement_url": "https://app.carbonmark.com/retirements/0x6fb0e62afB345951997bd5149a313df9fcB72C2d/33",
  "quote": {
    "uuid": "07495f6c-8b02-465d-8fdd-4bcc2d2047e9",
    "quantity_tonnes": 1,
    "cost_usdc": 3.78
  }
}
```

When the order status is `COMPLETED`, the carbon credit has been retired. You can also use `polygonscan_url` or `view_retirement_url` from the order response for additional confirmation.

## Retirement receipt

Once the order is complete, use the `view_retirement_url` returned by the API to access the retirement receipt.

The receipt URL follows this pattern:

```
https://app.carbonmark.com/retirements/{beneficiaryAddress}/{number}
```

Example:

```
https://app.carbonmark.com/retirements/0xab5b7b5849784279280188b556af3c179f31dc5b/33
```

The completed order will also appear in the Developer Dashboard, and the cost will be included in your monthly invoice.

## Notes

* Always use a versioned API base URL in production integrations
* Use `/prices` to resolve a valid listing `asset_price_source_id` before creating a quote
* Use `/quotes` to price the retirement
* Use `/orders` to submit the retirement
* Poll `/orders` with the `quote_uuid` until the order status is `COMPLETED`
* Some registries may require additional order metadata


# API Checkout Service

Use the Checkout Service API to create hosted checkout links for carbon credit purchases and retirements, then track payment and retirement status through the API.

The [Checkout Service is a REST API](https://checkout.carbonmark.com/) that creates unique checkout links powered by Stripe. Your application sends a listing-based `asset_price_source_id`, `quantity_tonnes`, retirement metadata, and redirect URLs, and the API returns a hosted checkout URL. When the user opens that URL and completes payment, the retirement flow is initiated.

This is the fastest path for teams that want to offer carbon credit purchases and retirements without building their own payment collection flow.

## When to use the Checkout Service

The Checkout Service is a good fit when you want to:

* offer carbon credit purchases and retirements in your application
* avoid building and maintaining a custom payment flow
* use a hosted checkout experience instead of collecting payment details directly
* get to market faster with a simpler integration

## How it works

At a high level, the flow is:

1. Your application sends a checkout request with an `asset_price_source_id`, `quantity_tonnes`, retirement metadata, and redirect URLs
2. The Checkout Service returns a hosted payment URL
3. The user opens the URL and completes payment
4. Your application reads the `payment_id` from the success redirect URL
5. Your application queries `/payments/{id}` to confirm payment and retirement status

## Core endpoints

The Checkout Service currently exposes these key endpoints:

* [`POST /checkout`](https://checkout.carbonmark.com/#/paths/checkout/post) — create a hosted checkout URL
* [`POST /quotes`](https://checkout.carbonmark.com/#/paths/quotes/post) — request a retirement quote for a given asset price source and quantity
* [`GET /payments/{id}`](https://checkout.carbonmark.com/#/paths/payments-id/get) — retrieve payment details and retirement status

## Checkout request requirements

To create a checkout URL, send a request to [`POST /checkout`](https://checkout.carbonmark.com/#/paths/checkout/post) with:

* `asset_price_source_id`
* `quantity_tonnes`
* `metadata`, including:
  * `beneficiary_name`
  * `retirement_message`
* `cancel_url`
* `success_url`

The `success_url` is suffixed with `/<payment_id>`. You can use that `payment_id` to query the `/payments` endpoint and confirm the result of the transaction.

## Availability and access

The Carbonmark API Checkout Service is available for use.

Use the endpoint reference to implement the hosted checkout flow and retrieve payment status through the API.


# Introduction to Carbonmark's x402 Endpoint

Retire tokenized carbon credits on Base directly from an AI agent or any HTTP client, with no inventory to manage and no SDK required.

The Klima x402 Endpoint is co-developed by Carbonmark. It exposes carbon retirement on the Base blockchain through the Klima Protocol Retirement Aggregator as plain HTTP calls: it discovers carbon liquidity, returns live price quotes, hands back unsigned `[approve, retire]` calldata, and resolves the public Carbonmark certificate once the transaction confirms.

The endpoint is built for the x402 agent-payments ecosystem and plugs directly into Base MCP, so an agent can read the catalog, prepare a retirement, and submit it through the user's Base Account wallet in a single approval.

## What you can do

With the x402 Endpoint, you can:

* Discover retirable carbon classes, credits, and reference prices on Base
* Retrieve live, on-chain quotes for a given tonnage in USDC or kVCM
* Receive unsigned `approve` + `retire` calldata to submit from your own wallet
* Retire gaslessly through a relay by signing a single token authorization
* Resolve the public Carbonmark certificate for any confirmed retirement
* Integrate carbon retirement into AI agents via the Klima Base MCP plugin

## Endpoint basics

| Property        | Value                                                                        |
| --------------- | ---------------------------------------------------------------------------- |
| GET base URL    | `https://x402.klimalabs.com/api/...`                                         |
| POST base URL   | `https://x402.klimalabs.com/api`                                             |
| Chain           | Base mainnet (`chainId=8453`)                                                |
| Auth            | Free GET or POST — on-chain protocol fee only                                |
| Current version | **v1** — pin `https://v1.x402.klimalabs.com` to stay on it                   |
| Agent manifest  | [`/.well-known/x402.json`](https://x402.klimalabs.com/.well-known/x402.json) |

All HTTP calls are free. Use **GET** with query parameters on action paths (`/discover`, `/quote`, and so on), or **POST** JSON to `/api` with an `action` field. Both return the same responses.

## Versioning and releases

These docs describe **v1**. The API is versioned by host, and the major version is the compatibility contract:

* `x402.klimalabs.com` always serves the **latest** release, so a future major bump moves it.
* `v1.x402.klimalabs.com` serves **v1 and every future 1.x release**, and never moves to v2.

If a breaking change reaching you unannounced would be a problem, **pin the `v1.` host**. That is the whole mechanism; there is no version header or query parameter.

Pinning does not freeze you out of new features. Additive work (new actions, new optional fields, new response fields) ships in minor releases and rolls into `v1` automatically. Only genuinely breaking changes, a request that used to succeed now failing, are held back and bundled into a single major bump so that you migrate once per major instead of continuously. When a new major ships, the previous one keeps answering at its own `v<major>` host during migration.

Every release is recorded in the machine-readable [changelog](https://x402.klimalabs.com/.well-known/x402-changelog.json), and each breaking one carries a `migration` string saying concretely what to change. Full detail is in the [reference](/carbonmark-x402/x402-reference#versioning).

## Two ways to retire

The endpoint supports two integration paths. Choose based on whether you want to submit the transaction yourself or have a relay do it for you.

* **Build-your-own** — `discover` → `quote` → `prepare/retire` hands back unsigned `[approve, retire]` calldata that **you** submit. Reads are free; you pay gas and broadcast the batch yourself.
* **Paid relay** — `prepare-auth` → sign one EIP-712 token authorization → `actions/retire`. A Klima executor relays the retirement on-chain and pays the gas, reimbursed from your signed budget. No native ETH and no Base Account required.

## The endpoints

| Action           | How to call                              | Moves funds?       | What it does                                                                         |
| ---------------- | ---------------------------------------- | ------------------ | ------------------------------------------------------------------------------------ |
| `discover`       | `GET /api/discover` or `POST /api`       | No                 | Lists carbon classes, credits, reference USDC/tonne prices, supported input tokens   |
| `quote`          | `GET /api/quote` or `POST /api`          | No                 | Live on-chain price for a tonnage (retirement cost + protocol fee + slippage buffer) |
| `prepare/retire` | `GET /api/prepare/retire` or `POST /api` | No (you broadcast) | Unsigned `[approve, retire]` batch for self-submit                                   |
| `prepare-auth`   | `GET /api/prepare-auth` or `POST /api`   | No                 | EIP-712 `typedData` + ready `actionsRetireRequest` for the relay path                |
| `actions/retire` | `POST /api`                              | Yes (relayed)      | Executor submits the retirement; requires signed `authPayload`                       |
| `certificate`    | `GET /api/certificate` or `POST /api`    | No                 | Resolves Carbonmark certificate URL(s) for a `txHash`                                |

## How it works

The typical build-your-own flow is:

{% stepper %}
{% step %}

#### Discover what's retirable

Call `/discover` to list carbon classes, credits, reference prices, and supported input tokens.
{% endstep %}

{% step %}

#### Get a live quote

Call `/quote` for the true cost of your tonnage, including the on-chain fee and slippage-buffered maximum input.
{% endstep %}

{% step %}

#### Prepare the retirement

Call `/prepare/retire` to receive the unsigned `approve` + `retire` batch, then submit it atomically from your wallet.
{% endstep %}

{% step %}

#### Resolve the certificate

After the transaction confirms, call `/certificate` to get the public Carbonmark certificate URL.
{% endstep %}
{% endstepper %}

You do not need to pre-purchase or hold inventory to get started.

## Getting started

1. Review the Endpoint basics above and confirm you are targeting Base mainnet (`chainId=8453`).
2. Follow the build-your-own walkthrough to complete your first retirement, or use the gasless relay path.
3. To integrate with an AI agent, connect the Klima Base MCP plugin.

{% content-ref url="/pages/qAv9U7k4sFVm8OnTXF2v" %}
[Retire carbon with x402](/carbonmark-x402/retire-carbon-with-x402)
{% endcontent-ref %}

{% content-ref url="/pages/InRBOZ3UT2BQ4oDzgkNI" %}
[Gasless retirement (paid relay)](/carbonmark-x402/gasless-retirement-paid-relay)
{% endcontent-ref %}

{% content-ref url="/pages/uLZsCsAs2JutV7TyulCj" %}
[Use x402 from an AI agent](/carbonmark-x402/use-x402-from-an-ai-agent)
{% endcontent-ref %}

{% content-ref url="/pages/BLFFpJupQHCgVRvN5ExO" %}
[x402 Reference](/carbonmark-x402/x402-reference)
{% endcontent-ref %}

{% hint style="warning" %}
Retirement is irreversible. A confirmed retirement permanently burns the carbon credit — there is no undo, refund, or resale once the transaction confirms. Always review the quoted tonnes, price, and fee before approving.
{% endhint %}

## Resources

* [x402 protocol](https://www.x402.org/) — the HTTP 402 Payment Required protocol this endpoint implements
* [Base MCP](https://docs.base.org/ai-agents/quickstart) — wallet-connected AI agents on Base mainnet
* [Agent plugin + setup docs](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation) — Base MCP setup guide, klima-retire plugin, and full integration docs
* [Discovery manifest](https://x402.klimalabs.com/.well-known/x402.json) — the public `x402.json` that agent crawlers index automatically


# Retire carbon with x402

Use the x402 Endpoint to discover carbon, retrieve a live quote, submit an unsigned `approve` + `retire` batch from your own wallet, and resolve the public certificate.

This page walks through the **build-your-own** path: reads are free, and you sign and broadcast the retirement transaction yourself on Base mainnet. If you would rather have a relay submit the transaction and pay gas for you, see [Gasless retirement (paid relay)](/carbonmark-x402/gasless-retirement-paid-relay).

All requests target Base mainnet (`chainId=8453`). Every action endpoint accepts **GET** with query parameters or **POST** JSON to `/api` with an `action` field; both return the same responses.

## Before you begin

You will need:

* A wallet on Base with a balance of an accepted input token (USDC or kVCM)
* Enough native ETH on Base to pay gas for the retirement transaction
* The ability to submit an atomic `approve` + `retire` batch (for example, via Base MCP `send_calls`)

See [x402 reference](/carbonmark-x402/x402-reference) for input token addresses, amount rules, and fees.

## Step 1: Discover what's retirable

Call `/discover` to list carbon classes and credits. Each class returns a **reference USDC/tonne price**, the credits inside it (registry, vintage, token, available liquidity), supported input tokens, and contract addresses.

Filters are optional and AND-combined — for example, `maxUsdcPricePerTonne=20` returns only classes at or below $20/tonne. `chainId` is **not** accepted on this endpoint.

Example request:

```bash
# List carbon classes (optional filters)
curl "https://x402.klimalabs.com/api/discover?maxUsdcPricePerTonne=15"
```

The POST equivalent sends the same optional filters:

```json
{ "action": "discover", "maxUsdcPricePerTonne": 15 }
```

{% hint style="warning" %}
**Reference price is not the price at size.** `priceUsdcPerTonne` is the marginal (spot) price and is accurate near 1 tonne. Large orders walk up the AAM curve — for example, a credit quoted at roughly $107.82/t for 1 t can cost roughly $386.61/t for 100 t once it consumes a large share of pool liquidity. Always call `/quote` for the true cost of your size.
{% endhint %}

## Step 2: Get a live quote

Call `/quote` with your input token, carbon class, and amount to get the real cost of your tonnage.

**Required parameters:**

* `chainId` (`8453`)
* `inputToken`
* `carbonClass`
* `amount` (decimal tonne string)

**Optional parameters:** `creditToken`, `vintage`, `tokenId`. When you do not pin a specific credit, the API selects the most liquid credit in the class that can cover the requested `amount`.

Example request:

```bash
curl "https://x402.klimalabs.com/api/quote?chainId=8453\
&inputToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\
&carbonClass=0xf4699531e0a5f6e9351a36de3753deaad329bf45&amount=1.5"
```

The response returns the retirement price, the on-chain `fee`, the `total` (price + fee), a `suggestedMaxInput` (total plus 4% slippage), a `humanSummary`, the `resolvedCredit` the server selected, and any `alternatives`.

Example response (trimmed for readability):

```json
{
  "tonnesFormatted": "1.5",
  "retirementPriceFormatted": "19.791336",
  "feeFormatted": "0.01",
  "totalFormatted": "19.801336",
  "suggestedMaxInputFormatted": "20.593389",
  "humanSummary": "1.5 tonnes @ 19.791336 USDC + 0.01 USDC fee = 19.801336 USDC (max 20.593389 USDC with 4% slippage)",
  "resolvedCredit": { "creditToken": "0xe662…71b8", "tokenId": 0, "vintage": 2021 }
}
```

## Step 3: Prepare the retirement

Call `/prepare/retire` with the same core parameters as `/quote`. The endpoint re-quotes on-chain and returns an **ordered batch**: an ERC-20 `approve` followed by the retirement, to be submitted atomically (for example, via Base MCP `send_calls`).

Optional parameters include `maxInputTokenIn` and `details` (a URL-encoded JSON object for certificate metadata; see below).

Example request:

```bash
curl "https://x402.klimalabs.com/api/prepare/retire?chainId=8453\
&inputToken=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\
&carbonClass=0xf4699531e0a5f6e9351a36de3753deaad329bf45&amount=1.5\
&details=%7B%22beneficiaryString%22%3A%22Acme%20Corp%22%7D"
```

{% hint style="info" %}
The `to` field and `approvalInstructions.spender` in the response are both the Settlement Contract. Read them from the response rather than hard-coding an address.
{% endhint %}

### Certificate metadata (`details`)

`details` is an optional URL-encoded JSON object. The schema is strict — unknown keys return `400`.

| Field                                                                                             | Meaning                                                                                                                                                                                                     |
| ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `retiringAddress`                                                                                 | Address performing the retirement                                                                                                                                                                           |
| `beneficiaryAddress`                                                                              | Address credited on the certificate. Defaults to the payer on this self-submit path; **required on the** [**relay path**](/carbonmark-x402/gasless-retirement-paid-relay) unless `beneficiaryIsPayer: true` |
| `beneficiaryString`                                                                               | Beneficiary display name — shows on the certificate                                                                                                                                                         |
| `retiringEntityString`                                                                            | Retiring-entity display name                                                                                                                                                                                |
| `retirementMessage`                                                                               | Public message on the certificate                                                                                                                                                                           |
| `beneficiaryLocation`, `consumptionCountryCode`, `consumptionPeriodStart`, `consumptionPeriodEnd` | Toucan Puro only — required for Puro credits                                                                                                                                                                |

{% hint style="warning" %}
**Set attribution up front.** `beneficiaryString` and `retirementMessage` are what make a certificate *named*, and the certificate **cannot be edited after the retirement confirms**. Note that the certificate's on-chain `retiringAddress` reflects an internal settlement/relayer address, not the `details.retiringAddress` you pass.
{% endhint %}

## Step 4: Submit the batch

Submit the returned `approve` + `retire` batch atomically from your wallet on Base. API calls are free; the protocol fee is settled on-chain inside this transaction, and the contract refunds any unused slippage budget in the same transaction.

## Step 5: Resolve the certificate

After the transaction confirms, call `/certificate` with the transaction hash to resolve the shareable Carbonmark certificate URL(s).

Use the optional `index` parameter to select one retirement out of a multi-retirement transaction; omit it to return all.

Example request:

```bash
curl "https://x402.klimalabs.com/api/certificate?txHash=0xYOUR_RETIRE_TX_HASH"
```

Example response (trimmed for readability):

```json
{
  "retirementCount": 1,
  "retirements": [{
    "certificateUrl": "https://app.carbonmark.com/retirements/id/8453-0x4a7f…f4bf-0",
    "amountInTonnes": "1",
    "beneficiaryName": "testing",
    "projectId": "UCR-423",
    "creditId": "UCR-423-2022"
  }]
}
```

{% hint style="info" %}
A `404 retirement_not_found` immediately after confirmation usually means the subgraph has not indexed the transaction yet. Wait a few seconds and retry.
{% endhint %}

## Notes

* Target Base mainnet only (`chainId=8453`).
* Reference prices from `/discover` approximate spot; always confirm real cost with `/quote`.
* When you do not pin a credit, the API picks the most liquid credit in the class that can cover your `amount`.
* Read the Settlement Contract address from the `/prepare/retire` response rather than hard-coding it.
* Set `beneficiaryString` and `retirementMessage` before retiring — certificates cannot be edited afterward.
* Retirement is irreversible once the transaction confirms.


# Gasless retirement (paid relay)

Retire carbon without holding native ETH, without a prior token approval, and without a Base Account — sign a single token authorization and a Klima executor submits the transaction for you.

The relay path lets any wallet or agent retire carbon by signing **one** standard EIP-712 token authorization. A Klima executor submits the on-chain transaction and is reimbursed for gas out of your signed budget. For the path where you submit the transaction yourself, see [Retire carbon with x402](/carbonmark-x402/retire-carbon-with-x402).

## What you sign

You sign the **token authorization, not the retirement**. The only signature is a standard token authorization:

* [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) `TransferWithAuthorization` for USDC, or
* [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) `Permit` for kVCM

This is exactly what any x402-style payment signs. Client signing is therefore plain `eth_signTypedData_v4`; there is no custom typed data to assemble.

On the **USDC** path the authorization's `nonce` is not random: it is `keccak256(retirement, salt)` — the exact credit, token id, amount, and full attribution struct being authorized, plus a fresh 32-byte `salt` the server mints and returns inside `actionsRetireRequest`. Because `nonce` is a signed EIP-3009 field, your signature covers **what gets retired and who is credited**, not just the spend value. `actions/retire` rebuilds the retirement, re-hashes it with the submitted `salt`, and returns `400 params_mismatch` on a mismatch.

**Post `actionsRetireRequest` back verbatim, `salt` included** (USDC). Without `salt` the authorization cannot be verified (`400 invalid_auth_payload`). The kVCM (Permit) path carries no commitment and no salt — Permit's nonce is the token's own counter.

## Before you begin

You will need:

* A wallet holding an accepted input token (USDC or kVCM) — **no ETH required**
* A client capable of `eth_signTypedData_v4`

## Attribution (required)

A relayed retirement must name its beneficiary. Pass one of:

* `details.beneficiaryAddress` — the party the retirement is for, or
* `beneficiaryIsPayer: true` — credit the paying wallet deliberately

Omitting both returns `400 attribution_required`. Attribution is permanently indexed on-chain and cannot be changed after confirmation.

## The flow

{% stepper %}
{% step %}

#### Prepare the authorization

`POST /api` with `prepare-auth`. The server resolves and prices the retirement and returns `typedData` (the EIP-712 object to sign) and `actionsRetireRequest` (a ready-to-send request body, including `salt` on the USDC path). Check `onChainDetails` before signing — it is exactly what the authorization commits to.
{% endstep %}

{% step %}

#### Sign the typed data

Sign `typedData` in your wallet with `signTypedData`. This is the only signing step.
{% endstep %}

{% step %}

#### Submit the retirement

`POST /api` with `actions/retire`, sending the request body plus your signature. A Klima executor relays the transaction on-chain.
{% endstep %}

{% step %}

#### Resolve the certificate

`POST /api` with `certificate` and the `txHash` to get the public proof. Poll if the transaction is still pending. A `settled` `actions/retire` response already includes certificate URL(s).
{% endstep %}
{% endstepper %}

```
1. POST /api  prepare-auth   → server resolves + prices, returns:
                                 • typedData            (EIP-712 object to sign)
                                 • actionsRetireRequest (ready-to-send body,
                                     incl. salt on the USDC path)
2. wallet     signTypedData(typedData)        ← the ONLY signing step
3. POST /api  actions/retire (body + signature) → executor relays on-chain
4. POST /api  certificate    { txHash }       → public proof (poll if pending)
```

{% hint style="info" %}
`prepare-auth` is the `200` alias of the `402` challenge that `actions/retire` returns when it is posted **without** an `authPayload`. Either entry point gives you the same `typedData`.
{% endhint %}

## How the signed budget works

The signed budget (`authValue`) covers the retirement, the protocol fee, and the executor's gas reimbursement, with a slippage buffer. The signer needs only an input-token balance (USDC or kVCM) — no ETH. Send the `actionsRetireRequest` body **verbatim** so that `from`, `to`, and (on USDC) `salt` match the signed authorization.

## `actions/retire` responses

| Status                              | Meaning                                                                                                                                              |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `settled`                           | Mined and indexed — `retirements[]` carries the certificate URL(s).                                                                                  |
| `pending_index`                     | Mined or broadcast, but the subgraph has not caught up — poll `/certificate` with the tx hash.                                                       |
| `400 attribution_required`          | No beneficiary named — set `details.beneficiaryAddress` or `beneficiaryIsPayer: true`.                                                               |
| `400 insufficient_authorized_value` | Your signed `value` no longer covers retirement + fee + gas (price or gas moved). Re-run `prepare-auth` and re-sign.                                 |
| `400 invalid_auth_payload`          | `from` ≠ request `from`, `to` ≠ settlement contract, wrong payload shape, or USDC path missing `salt`. Use the `actionsRetireRequest` body verbatim. |
| `400 params_mismatch`               | Submitted retirement does not match the authorized USDC nonce commitment. Re-post verbatim or re-prepare and re-sign.                                |
| `422 transaction_reverted`          | Mined but reverted on-chain; no retirement recorded.                                                                                                 |

See [x402 reference](/carbonmark-x402/x402-reference) for the full error registry.

## Reference clients

* **SDK** — zero-dependency TypeScript client [`sdk/klima-retire.ts`](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation/blob/main/sdk/klima-retire.ts). One `retire()` call runs prepare-auth → sign → submit → poll certificate. Pass `beneficiaryIsPayer: true` or `details.beneficiaryAddress`.
* **Examples** — runnable scripts under [`examples/`](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation/tree/main/examples) (`retire/retire-sdk.ts`, `retire/paid-retire.ts`, `retire/retire-raw.ts`).

## Notes

* You sign a token authorization, not the retirement itself — and on USDC that authorization binds the retirement via `nonce` + `salt`.
* The signer needs only an input-token balance — no ETH.
* Name a beneficiary (`details.beneficiaryAddress` or `beneficiaryIsPayer: true`) before signing.
* Re-run `prepare-auth` and re-sign if you hit `insufficient_authorized_value`; do not blind-retry.
* Send the `actionsRetireRequest` body verbatim (including `salt` on USDC) to keep the signature valid.
* Retirement is irreversible once the transaction confirms.


# Use x402 from an AI agent

Wire the x402 Endpoint into any Base MCP–capable AI agent so it can discover, quote, prepare, and submit a carbon retirement from a plain-language request.

The x402 Endpoint is built for the x402 agent-payments ecosystem and plugs directly into Base MCP. The fastest path is the Klima Base MCP plugin, which connects the endpoint to agents such as Claude Code.

## Setup

{% stepper %}
{% step %}

#### Install

Connect Base MCP and add the Klima plugin by following the [Base MCP setup guide](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation/blob/main/base-mcp-setup.md). The plugin definition is [`plugins/klima-retire.md`](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation/blob/main/plugins/klima-retire.md).
{% endstep %}

{% step %}

#### Ask in plain language

For example: "Retire 2 tonnes of carbon under $15/tonne, beneficiary 'Acme Corp'."
{% endstep %}

{% step %}

#### Approve once

The agent runs `discover` → `quote` → `prepare/retire`, shows you the cost, submits a single wallet approval, and returns your Carbonmark certificate URL.
{% endstep %}
{% endstepper %}

## Prefer a direct integration?

You do not need MCP to use the endpoint. You can:

* Call the GET or POST endpoints yourself, then sign and submit the `prepare/retire` batch (`approve` + retirement) with your own wallet on Base — see [Retire carbon with x402](/carbonmark-x402/retire-carbon-with-x402).
* Use the gasless relay path — sign one authorization and let a Klima executor submit it — see [Gasless retirement (paid relay)](/carbonmark-x402/gasless-retirement-paid-relay).

## Agent discovery

x402 directories and agent crawlers can index the endpoint automatically through its public well-known documents:

| Document       | URL                                                                                              |
| -------------- | ------------------------------------------------------------------------------------------------ |
| Manifest       | [`/.well-known/x402.json`](https://x402.klimalabs.com/.well-known/x402.json)                     |
| Error registry | [`/.well-known/x402-errors.json`](https://x402.klimalabs.com/.well-known/x402-errors.json)       |
| Changelog      | [`/.well-known/x402-changelog.json`](https://x402.klimalabs.com/.well-known/x402-changelog.json) |
| Docs index     | [`/api/docs`](https://x402.klimalabs.com/api/docs)                                               |

## Resources

* [Agent plugin + setup docs](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation) — Base MCP setup guide, klima-retire plugin, and full integration docs
* [Base MCP setup guide](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation/blob/main/base-mcp-setup.md) — connect Base MCP and add the Klima retirement plugin
* [Base MCP](https://docs.base.org/ai-agents/quickstart) — Base AI agent quickstart for wallet-connected agents on Base mainnet
* [Klima Aggregator USAGE](https://github.com/KlimaDAO/retirement-aggregator/blob/main/USAGE.md) — direct on-chain access for callers who do not want the HTTP layer

{% hint style="warning" %}
Retirement is irreversible. Review the tonnes, price, and fee the agent presents before approving — a confirmed retirement cannot be undone, refunded, or resold.
{% endhint %}


# x402 Reference

Contract addresses, input tokens, amount rules, fees, endpoints, and the full error code reference for the x402 Endpoint on Base mainnet.

This page is shared reference material for both the [build-your-own](/carbonmark-x402/retire-carbon-with-x402) and [gasless relay](/carbonmark-x402/gasless-retirement-paid-relay) paths.

## Endpoints

| Action           | How to call                              | Moves funds?       | What it does                                                                         |
| ---------------- | ---------------------------------------- | ------------------ | ------------------------------------------------------------------------------------ |
| `discover`       | `GET /api/discover` or `POST /api`       | No                 | Lists carbon classes, credits, reference USDC/tonne prices, supported input tokens   |
| `quote`          | `GET /api/quote` or `POST /api`          | No                 | Live on-chain price for a tonnage (retirement cost + protocol fee + slippage buffer) |
| `prepare/retire` | `GET /api/prepare/retire` or `POST /api` | No (you broadcast) | Unsigned `[approve, retire]` batch for self-submit                                   |
| `prepare-auth`   | `GET /api/prepare-auth` or `POST /api`   | No                 | EIP-712 `typedData` + ready `actionsRetireRequest` for the relay path                |
| `actions/retire` | `POST /api`                              | Yes (relayed)      | Executor submits the retirement; requires signed `authPayload`                       |
| `certificate`    | `GET /api/certificate` or `POST /api`    | No                 | Resolves Carbonmark certificate URL(s) for a `txHash`                                |

All HTTP calls are free. Use **GET** with query parameters or **POST** JSON to `/api` with an `action` field. Both return the same responses. Live schemas and per-action `errorCodes` are in the [discovery manifest](https://x402.klimalabs.com/.well-known/x402.json).

## Versioning

The API carries a semantic version, published as `apiVersion`. The **major** is the compatibility contract, and the **host** is how you pin it.

### Pinning a major

| Host                    | Serves                                                                    |
| ----------------------- | ------------------------------------------------------------------------- |
| `x402.klimalabs.com`    | the **latest** release. A breaking release moves it to the new major.     |
| `v1.x402.klimalabs.com` | the **v1 major** and every future 1.x minor and patch. Never moves to v2. |
| `v0.x402.klimalabs.com` | the previous major, kept reachable for callers that have not migrated.    |

These docs describe v1. **Pin `v1.x402.klimalabs.com`** if you do not want the bare host's future major bump to reach you. Pinning is the whole mechanism — there is no version header, query parameter, or `Accept` negotiation.

Resolve it programmatically instead of hard-coding: `/.well-known/x402.json` and `/.well-known/x402-changelog.json` both publish `apiVersion` and a `versionedHosts` object (`latest`, `current`, and a `note`), so an agent can find its major's host without reading this table.

### What lands in a major

Releases bundle by compatibility, not by date or size:

| Bump                      | Contains                                                                      | Reaches a pinned caller?             |
| ------------------------- | ----------------------------------------------------------------------------- | ------------------------------------ |
| **Patch** (1.0.0 → 1.0.1) | Fixes that leave the request and response contract intact.                    | Yes — rolls into `v1` automatically. |
| **Minor** (1.0 → 1.1)     | Additive only: new actions, new optional request fields, new response fields. | Yes — rolls into `v1` automatically. |
| **Major** (1.x → 2.0)     | Anything a working caller could notice as a break.                            | **No** — `v1` stays where it is.     |

Breaking means: a request that succeeded before now fails, or a response field is removed, renamed, or changes meaning. Newly *required* fields and stricter validation both count — the two breaking releases so far were a newly required attribution field and a body that stopped silently discarding unknown top-level keys.

Additive work does **not** wait for the next major. It ships in a minor and reaches every 1.x caller, so pinning `v1` does not freeze you out of new features — it only protects you from `v2`.

Everything that accumulates as breaking is held until one deliberate major bump rather than trickling out, so you migrate once per major. Each release appears in the changelog with a `breaking` boolean, and every breaking one carries a `migration` string stating concretely what a caller must change. **The changelog is the machine-readable record of that bundling** — poll `/.well-known/x402-changelog.json` and read `migration` rather than diffing responses.

When a new major ships, the previous one keeps answering at its own `v<major>` host. It is not extended, only kept reachable, so treat it as a migration window and not a long-term target.

## Inputs and contracts (Base mainnet)

| Contract              | Address                                      |
| --------------------- | -------------------------------------------- |
| Input token: USDC     | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` |
| Input token: kVCM     | `0x00fbac94fec8d4089d3fe979f39454f48c71a65d` |
| Retirement Aggregator | `0xda0a793d7c32ab80bcdab7f8c725c96db22464f4` |
| Klima Protocol AAM    | `0x1C24239309398220883207681602BfF4D10fbde1` |
| Settlement Contract   | `0x290e98e95dacE244c73376C0c39A4D53b22E34B6` |

Prefer reading the Settlement Contract from each `prepare` / `prepare-auth` response (`to` / `approvalInstructions.spender`) rather than hard-coding it. The address above is the current Base mainnet value from `api/src/config.ts`.

## Amount rules

* Amounts are decimal tonne strings, with a minimum of `0.001` t (1 kg).
* Toucan Puro credits retire in **whole tonnes only**.
* Amounts above a credit's available liquidity are rejected.

## Fees

API calls are free. Each retirement bakes in a protocol fee, computed and collected on-chain by the Settlement Contract:

* The fee is `max(floor, feeBps% of cost)`.
* The floor is denominated in USDC (converted to kVCM via the pool when paying in kVCM).
* It is always included in `quote.fee` and folded into `quote.total`.
* The contract spends exactly `retirementCost + fee` and refunds any unused slippage budget in the same transaction.

## Error reference

Failures return JSON with an `error` code plus actionable fields:

```json
{ "error": "<code>", "message": "…", "x402FacilitatorVersion": 2 }
```

plus code-specific context (`issues` on `schema_validation`, `expectedNonce` / `receivedNonce` on `params_mismatch`, and so on). **Match on `error`, never on `message`** — codes are stable, wording is not.

`retryable: yes` means the identical request can succeed later untouched. `retryable: no` means fix the request first.

| Code                            | HTTP | Group         | Retryable | Meaning and remedy                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| ------------------------------- | ---- | ------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_json`                  | 400  | request       | no        | The request body was not parseable JSON. Send a JSON object with a `content-type: application/json` header. Note the API takes a single POST body, not form-encoded fields.                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `unknown_action`                | 400  | request       | no        | The body's `action` field is missing or is not one of the supported actions. Set `action` to one of the values in `supported` (echoed in the error body), or GET the endpoint root for the action index.                                                                                                                                                                                                                                                                                                                                                                                                        |
| `not_found`                     | 404  | request       | no        | No route exists at the requested path. This API is a single POST multiplexer: POST the endpoint URL with an `action` field rather than using per-action paths. The 404 body carries the endpoint and action list.                                                                                                                                                                                                                                                                                                                                                                                               |
| `document_not_found`            | 404  | request       | no        | No documentation document with the requested `id` (from `/api/docs?id=…`). Use one of the ids in the error's `available` list, or fetch the index at /api/docs.                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `schema_validation`             | 400  | request       | no        | The body failed schema validation. `issues` carries the offending path and reason. Bodies are strict at the top level and inside `details`, so an unrecognized key is an error rather than being silently dropped. Read `issues[].path` and `issues[].keys`. For `unrecognized_keys`, check the key belongs where you put it — attribution fields go inside `details`, not at the top level.                                                                                                                                                                                                                    |
| `internal_error`                | 500  | request       | yes       | An unhandled server-side failure. Retry with backoff. If it persists, report it via the contact in /.well-known/security.txt with the request body.                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `unsupported_chain_id`          | 400  | resolution    | no        | `chainId` is not a supported network. Use 8453 (Base mainnet) or 84532 (Base Sepolia).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `unsupported_input_token`       | 400  | resolution    | no        | `inputToken` is not an accepted payment token on this chain. Use the USDC or kVCM address for the chain — see the manifest, or the addresses in the endpoint documentation.                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `invalid_input_token`           | 400  | resolution    | no        | `inputToken` passed validation but matches neither settlement path (EIP-3009 USDC nor EIP-2612 kVCM), so no relay function applies. Use the chain's USDC or kVCM address.                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `no_candidates`                 | 404  | resolution    | yes       | No credit in the carbon class matched the request filters, or the class holds no credits. Call `discover` to list live classes and credits, then retry with a `carbonClass`/`creditToken` from that response. Retryable because class inventory changes.                                                                                                                                                                                                                                                                                                                                                        |
| `vintage_not_found`             | 400  | resolution    | no        | No credit in the class carries the requested `vintage`. Pick one of the years in the error's `availableVintages`, or omit `vintage` to let the server choose a liquid credit.                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `insufficient_liquidity`        | 422  | amount        | yes       | The pool cannot fill the requested amount at any price right now. Reduce `amount`, choose another credit or class, or retry later. Retryable because pool depth changes block to block.                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `amount_not_whole_tonnes`       | 422  | amount        | no        | The credit's registry (Puro) retires in whole tonnes only, and `amount` has a fractional part. Send an integer `amount` (e.g. "2", not "2.5").                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `amount_below_increment`        | 422  | amount        | no        | `amount` is smaller than the credit's minimum retirement unit. Raise `amount` to at least the minimum reported in the error body.                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `puro_details_required`         | 400  | amount        | no        | The credit is Puro-issued, whose registry requires consumption metadata that the request omitted. Add the fields named in the error body to `details`: `beneficiaryLocation`, `consumptionCountryCode`, `consumptionPeriodStart`, `consumptionPeriodEnd`.                                                                                                                                                                                                                                                                                                                                                       |
| `payment_required`              | 402  | authorization | no        | Not a failure: the x402 challenge returned when `actions/retire` is posted without an `authPayload`. The body carries the EIP-712 `typedData` to sign and a ready-to-send `actionsRetireRequest`. Identical in shape to a `prepare-auth` 200. Sign `typedData` with the payer wallet, set `authPayload.signature` (or `v`/`r`/`s`), and POST `actionsRetireRequest` back — verbatim, including `salt` on the USDC path.                                                                                                                                                                                         |
| `attribution_required`          | 400  | authorization | no        | A relayed retirement named no beneficiary. The beneficiary is indexed on-chain as a permanent grouping key and cannot be changed once the retirement confirms, so it is not defaulted silently. Set `details.beneficiaryAddress` to the party the retirement is for, or set `beneficiaryIsPayer: true` to credit the paying wallet deliberately.                                                                                                                                                                                                                                                                |
| `invalid_auth_payload`          | 400  | authorization | no        | The authorization is structurally wrong for this request: `authPayload.from` is not the request `from`, `authPayload.to` is not the settlement contract, the payload shape doesn't match the input token's scheme (EIP-3009 for USDC, EIP-2612 for kVCM), or a USDC payload arrived without its top-level `salt`. Post the `actionsRetireRequest` from `prepare-auth` (or the 402 challenge) verbatim, adding only the signature. Do not rebuild the payload by hand.                                                                                                                                           |
| `insufficient_authorized_value` | 400  | authorization | no        | The signed `authPayload.value` no longer covers retirement + protocol fee + executor gas, usually because price or gas moved after signing. Relaying it would revert on-chain. Re-run `prepare-auth` (or re-request the 402 challenge) to size a fresh budget of at least `requiredMinimum`, then re-sign. The old authorization is unusable, not merely stale.                                                                                                                                                                                                                                                 |
| `params_mismatch`               | 400  | authorization | no        | The submitted retirement is not the one that was authorized. On the USDC path `authPayload.nonce` is keccak256 of the retirement plus `salt`, so the signature binds the credit, amount, and attribution — not just the spend value. The rebuilt struct hashed to something else. Re-post `actionsRetireRequest` verbatim including `creditToken`, `tokenId`, `details`, and `salt`, or re-run `prepare-auth` and re-sign. A salt is single-use; one from an earlier authorization will not reproduce the nonce. The error echoes `expectedNonce`, `receivedNonce`, and the `submitted` values to diff against. |
| `contract_revert`               | 422  | settlement    | yes       | A contract call reverted during simulation, so nothing was broadcast and no funds moved. `selector` and `decoded.errorName` identify the revert; `contract`, `function`, and `args` give the call context. Read `decoded.errorName`. Liquidity and slippage reverts are worth retrying with a fresh quote; validation and permission reverts are not.                                                                                                                                                                                                                                                           |
| `transaction_reverted`          | 422  | settlement    | yes       | The relayed transaction mined but reverted, typically from a state change between simulation and inclusion. No retirement was recorded. Inspect `transactionHash` on a block explorer, then re-run `prepare-auth` and re-sign. The old authorization's nonce may already be consumed.                                                                                                                                                                                                                                                                                                                           |
| `retirement_not_found`          | 404  | settlement    | yes       | No indexed retirement for that transaction hash. Immediately after confirmation this means the subgraph has not caught up yet, not that the retirement failed. Poll every few seconds. If a retirement response returned `pending_index`, this is the expected interim state.                                                                                                                                                                                                                                                                                                                                   |
| `gas_estimate_unavailable`      | 503  | upstream      | yes       | The executor's gas reimbursement could not be priced, so the authorization budget cannot be sized. No retirement was attempted. Retry with backoff. Nothing was signed or spent, so the request can be repeated unchanged.                                                                                                                                                                                                                                                                                                                                                                                      |

### Which action returns what

| Action           | Codes specific to it                                                                                                                                                                                                                                                                                                                                                                                                  |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `discover`       | —                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `quote`          | `unsupported_chain_id`, `unsupported_input_token`, `no_candidates`, `vintage_not_found`, `insufficient_liquidity`, `amount_not_whole_tonnes`, `amount_below_increment`, `contract_revert`                                                                                                                                                                                                                             |
| `prepare/retire` | `unsupported_chain_id`, `unsupported_input_token`, `no_candidates`, `vintage_not_found`, `insufficient_liquidity`, `amount_not_whole_tonnes`, `amount_below_increment`, `puro_details_required`, `contract_revert`                                                                                                                                                                                                    |
| `prepare-auth`   | `unsupported_chain_id`, `unsupported_input_token`, `no_candidates`, `vintage_not_found`, `insufficient_liquidity`, `amount_not_whole_tonnes`, `amount_below_increment`, `puro_details_required`, `attribution_required`, `contract_revert`, `gas_estimate_unavailable`                                                                                                                                                |
| `actions/retire` | `unsupported_chain_id`, `unsupported_input_token`, `invalid_input_token`, `no_candidates`, `vintage_not_found`, `insufficient_liquidity`, `amount_not_whole_tonnes`, `amount_below_increment`, `puro_details_required`, `payment_required`, `attribution_required`, `invalid_auth_payload`, `insufficient_authorized_value`, `params_mismatch`, `contract_revert`, `transaction_reverted`, `gas_estimate_unavailable` |
| `certificate`    | `retirement_not_found`                                                                                                                                                                                                                                                                                                                                                                                                |

Every action can additionally return: `invalid_json`, `unknown_action`, `not_found`, `document_not_found`, `schema_validation`, `internal_error`.

The same data is served live at [`/.well-known/x402-errors.json`](https://x402.klimalabs.com/.well-known/x402-errors.json) (GET alias `/api/errors`) — build error handling against that document rather than against this page.

For relay-specific `actions/retire` status values (`settled`, `pending_index`), see [Gasless retirement (paid relay)](/carbonmark-x402/gasless-retirement-paid-relay).

## Resources

* [Paid-retire examples](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation/tree/main/examples) — runnable end-to-end relay clients (USDC + kVCM)
* [TypeScript SDK](https://github.com/KlimaDAO/Klima-Protocol-x402-MCP-documentation/blob/main/sdk/klima-retire.ts) — zero-dependency `retire()` client
* [Klima Retirement Aggregator USAGE](https://github.com/KlimaDAO/retirement-aggregator/blob/main/USAGE.md) — direct on-chain access for callers who do not want the HTTP layer
* [Discovery manifest](https://x402.klimalabs.com/.well-known/x402.json) — public `x402.json` for agent directories
* [Changelog](https://x402.klimalabs.com/.well-known/x402-changelog.json) — machine-readable release history


# Direct Credit Issuance

## Overview

**Carbonmark Direct** provides a pathway for innovative carbon projects that employ methodologies that may not be supported by existing carbon registries to utilize Carbonmark’s blockchain-based infrastructure for credit issuance, retirement, and lifecycle tracking. We thus provide an option for carbon project developers to issue directly to our platform.

Carbonmark wallets are utilized to manage an inventory of issued credits. These credits will be represented natively by a balance of “CMARK” tokens recorded on an immutable public blockchain and conveniently managed, sold, or retired by users of the Carbonmark platform.

Every balance of credits will be represented by a unique CMARK project identifier (“CMARK-1”) and a credit vintage (“2024”), which are combined to form a unique credit identifier (“CMARK-1-2024”).

Each credit identifier is tied to a public blockchain address for audibility and transparency concerning the provenance of credit issuance, transfers, and retirements. This address can be used to view the current state on any third-party indexing service (e.g., polygonscan.com or blockscout.com).

## Key Project Information

Project pages for credits issued under Carbonmark’s direct credit issuance module include similar information to that found on our standard carbon project pages. This information includes:

* Project geography and geolocation.
* Project descriptions and summaries.
* Methodologies, standards, and relevant documentation.
* Related Sustainable Development Goals.
* Images or media provided by the project developers.
* Credit availability and issued vintages.

Additionally, within the Project Documentation information section, we provide publicly available materials related to the following:

* Project design and methodology documentation.
* Monitoring period information and associated verification documents.
* Supporting documentation, including location files, credit ownership agreements, etc.

Importantly, our project pages provide information on the third-party validation and verification body (VVB) that conducted analyses on our issued credits. Note that, unless marked otherwise, Carbonmark only collaborates with project developers that utilize third-party VVBs recognized by leading ICROA-endorsed registry standards, in addition to those accredited by the [ANSI National Accreditation Board for GHG Validation and Verification](https://anabpd.ansi.org/Accreditation/environmental/greenhouse-gas-validation-verification/AllDirectoryListing?prgID=200\&statusID=4).

## Getting Started

The first step to joining Carbonmark as a project developer issuing credits natively within our system is to complete and submit our [Carbonmark Direct Onboarding](https://share-eu1.hsforms.com/1eaZal2LjRdCkeaWaAQ1R7wfhhlr) form to register your interest.

A few pieces of required information are necessary for our team to conduct initial due diligence on your carbon project. This includes an overview of your project technology type, methodology, stage of PDD development (i.e., is the PDD complete, in progress, etc), and information on the third-party VVB being utilized for validation and verification work. Furthermore, information on the expected volume of credits issued and what monitoring periods they follow is requested.

After our initial review, we will set up a more comprehensive discussion to learn more about your project and provide additional background on the Carbonmark platform and our role in the market.

Overall, the flow of activities is as follows:

1. Initial review and registration of interest
2. KYB/KYC onboarding
3. Kick-off call to discuss our review and provide an overview of the Carbonmark platform
4. Analysis of additional required materials
5. Official registration of the project
6. Carbon credit intake form
7. Credit issuance

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2F9OAbhzLNfpWcMU2KAbvU%2FCreditIssuance_2.png?alt=media&amp;token=1ef5a7e1-9e83-48c9-8c40-b22ae12394cf" alt=""><figcaption></figcaption></figure>

## Smart contracts verified as secure and robust

At Carbonmark, the security and reliability of our platform are our highest priorities. We are committed to providing a safe and transparent experience for our partners. That's why we partnered with Hashlock to perform a security audit.\
\
Hashlock audited the solidity code within the Carbonmark project, the scope of work included a comprehensive review of the smart contracts (CreditToken.sol, CreditTokenFactory.sol, Deploy.sol, Errors.sol and Validate.sol smart contracts). They tested the smart contracts to check for their security and efficiency.

After Hashlock’s Audit, the team found the smart contracts to be “Secure.” The [audit report](https://hashlock.com/audits/carbonmark) confirms that Carbonmark’s smart contracts meet top industry security standards.


# Transfer Credits to/from Carbonmark

This section provides details for how to transfer carbon credits both to and from Carbonmark for the following registries:

* [International Carbon Registry](https://www.carbonregistry.com/) (ICR)
* [puro.earth](https://puro.earth/)
* [EcoRegistry](https://www.ecoregistry.io/) (coming soon)

### One-way Bridged Credits

Credits from Verra's VCS carbon certification standard were transferred to our platform using a one-way process to avoid double counting as Verra's registry infrastructure did not support transfers of credits outside of its system.

The one-way process utilized was akin to the process undertaken when converting CDM certified credits to VCS or Gold Standard, whereby credits were cancelled in the host registry and then re-issued on a separate system.

In our case, legacy VCS credits currently listed on Carbonmark were cancelled in Verra's registry and re-issued on our underlying distributed ledger system by Toucan Protocol. Therefore, these credits cannot be sent back to a Verra registry account.

for Importantly, the environmental benefit of these credits was not claimed during the original transfer process, and we provide traceability tooling to link all VCS credits on our platform to the original transfer event on Verra's registry.


# ICR: Import Credits to Carbonmark

## Connect to ICR

1. Navigate to your [**Portfolio**](https://app.carbonmark.com/portfolio) page and log in to your Carbonmark account.
2. Click the `IMPORT CARBON CREDITS` button.
3. `Select International Carbon Registry` and click `NEXT.`
4. Click the `CONNECT TO ICR` button.

{% hint style="info" %}
If you are not already logged in to your ICR account, you will be prompted to login with your username and password.
{% endhint %}

5. A prompt will ask if you want to install Carbonmark. This action will install the Carbonmark app on your ICR organization, along with the necessary permissions.

<div align="center"><figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FH6XttB9NWalIJwZePmOZ%2FScreen%20Shot%202024-07-23%20at%209.40.36%20AM.png?alt=media&amp;token=85bdd535-cdd1-4a7a-ac3d-da1b099da416" alt="" width="303"><figcaption></figcaption></figure></div>

6. Accept the necessary permissions for the Carbonmark app and click `Install`.\
   \
   These permissions **do not** give Carbonmark the permission to unilaterally transfer credits on your behalf from your ICR account. You will still need to approve requested credit transfers to your Carbonmark account from your ICR organization .

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2Fq9OAOPUgR5kYZ6Ii5Soe%2FScreen%20Shot%202024-07-23%20at%209.43.57%20AM.png?alt=media&amp;token=c15cf9a0-f1ee-4d32-ba9d-2b19158756e7" alt="" width="373"><figcaption></figcaption></figure>

7. If installation is successful, you will see this message: `Successfully connected to ICR, you may now close this window`. Close the window and go back to <https://app.carbonmark.com/portfolio>.

{% hint style="info" %}
If you have previously installed the Carbonmark app on your organization, you will be prompted again to install the Carbonmark app when revisiting <https://app.carbonmark.com/portfolio> and executing the steps above. Click `Install` to connect to ICR and ignore the notification that the app is already installed.
{% endhint %}

## Request Credit Transfer from ICR

1. Once the Carbonmark app has been installed on your organization, go to <https://app.carbonmark.com/portfolio>, select `IMPORT CARBON CREDITS` first, then `International Carbon Registry`, and finally click `NEXT`.
2. You will now see the `Import credits` screen. You will be able to access all of your organization's ICR credits and their respective amounts as seen on the screenshot below.

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FCgM3df9JZT9HyE0adq3a%2Ficrimport.png?alt=media&amp;token=911e4ad4-140d-420c-8d3f-c0bb92c9df86" alt=""><figcaption></figcaption></figure>

3. Select the asset for which you would like to issue a transfer request.
4. Enter a `Quantity` to determine how many credits of the selected type you want to import to Carbonmark.
5. Click `TRANSFER CREDITS`.
6. A popup will inform you that the transfer has been requested successfully. Click the link contained in that message to be taken directly to your organization's ICR inventory.
7. All requests will appear in your organization's inventory page, including past accepted and declined requests. Unconfirmed transfers will show an `Accept` button as highlighted on the screenshot below.

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FVniJlbTNYh8cBbfMQ1V6%2Ficrbuttons.png?alt=media&amp;token=e40526fe-3fd7-4e02-b07e-3d2f88ddb436" alt=""><figcaption></figcaption></figure>

6. Click the mentioned `Accept` button.
7. You will see a message which asks you once more to `Accept` the transfer. Once you click the button, the request will be fulfilled and the credits will be sent to the address highlighted on the screenshot below (which should be your Carbonmark Wallet address).

{% hint style="danger" %}
**IMPORTANT**: **If credits are sent to an incorrect Carbonmark Wallet address**, those credits are <mark style="color:red;">**irretrievable**</mark>.

While the correct address should be selected automatically by our systems, <mark style="color:red;">**PLEASE**</mark> <mark style="color:red;">**double check the account address**</mark> shown to you to confirm you have control of that account address. When in doubt, issue a request for a small amount first to confirm the connection. The smallest amount you can request is 0.001 credits.
{% endhint %}

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FcN0SikZmdiVG7NnUGF9I%2Ficraddress.png?alt=media&amp;token=2d5b81e2-53ea-40dc-90fb-32d11f93b924" alt=""><figcaption><p>The Carbonmark Wallet address associated with your Carbonmark account.</p></figcaption></figure>

### Completing Open Requests

You can complete open requests **at any later date** by navigating to your ICR's organization's `Inventory` page as seen on the screenshot below.

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2F9RQgQ4aiLPTxJv4McBa3%2Ficrinventory.png?alt=media&amp;token=530fb4e4-a4c1-4c27-acd5-ad42ac7abab4" alt=""><figcaption></figcaption></figure>

## Find Your ICR Credits in Carbonmark

Once a transfer request on ICR's app has been completed, go back to <https://app.carbonmark.com/portfolio>. The transfer of your assets usually takes a few seconds, but depending on network load it might take a few minutes. Once the transfer is completed, your credits will appear in your portfolio. Try refreshing the page if they do not show up.


# ICR: Export Credits to ICR

## Connect to ICR

1. Navigate to <https://app.carbonmark.com/portfolio> and log in to your Carbonmark account.
2. Click the `EXPORT CARBON CREDITS` button.
3. `Select International Carbon Registry` and click `NEXT.`
4. Click the `CONNECT TO ICR` button.

{% hint style="info" %}
If you are not already logged in to your ICR account, you will be prompted to login with your username and password.
{% endhint %}

5. A prompt will ask if you want to install Carbonmark. This action will install the Carbonmark app on your ICR organization, along with the necessary permissions.

<div align="center"><figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FH6XttB9NWalIJwZePmOZ%2FScreen%20Shot%202024-07-23%20at%209.40.36%20AM.png?alt=media&amp;token=85bdd535-cdd1-4a7a-ac3d-da1b099da416" alt="" width="303"><figcaption></figcaption></figure></div>

6. Accept the necessary permissions for the Carbonmark app and click `Install`.\
   \
   These permissions **do not** give Carbonmark the permission to unilaterally transfer credits on your behalf from your ICR account. You will still need to approve requested credit transfers to your Carbonmark account from your ICR organization .

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2Fq9OAOPUgR5kYZ6Ii5Soe%2FScreen%20Shot%202024-07-23%20at%209.43.57%20AM.png?alt=media&amp;token=c15cf9a0-f1ee-4d32-ba9d-2b19158756e7" alt="" width="373"><figcaption></figcaption></figure>

7. If installation is successful, you will see this message: `Successfully connected to ICR, you may now close this window`. Close the window and go back to <https://app.carbonmark.com/portfolio>.

{% hint style="info" %}
If you have previously installed the Carbonmark app on your organization, you will be prompted again to install the Carbonmark app when revisiting <https://app.carbonmark.com/portfolio> and executing the steps above. Click `Install` to connect to ICR and ignore the notification that the app is already installed.
{% endhint %}

## Transfer Credits to ICR Organization

1. Once the Carbonmark app has been installed on your organization, go to <https://app.carbonmark.com/portfolio>, select `EXPORT CARBON CREDITS` first, then `International Carbon Registry`, and finally click `NEXT`.
2. You will now see the `Export credits` screen. You will be able to access all ICR credits stored in your Carbonmark Wallet and their respective amounts as seen on the screenshot below.

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FJzR5j2oakUwvGkVS0rqt%2Ficrexport.png?alt=media&amp;token=7301e73f-317f-4121-b7ab-7c5c15e250fc" alt=""><figcaption></figcaption></figure>

3. Select the asset you would like to transfer to your ICR organization.
4. Enter a `Quantity` to determine how many credits of the selected type you want to export to your ICR organization.
5. Click `TRANSFER CREDITS`.
6. You will be prompted to enter the PIN associated with your Carbonmark account to finalize the transfer.

{% hint style="danger" %} <mark style="color:red;">**This transaction is irreversible.**</mark> Ensure you are connected to the correct ICR account **before** issuing a credit transfer. When in doubt, **transfer a small amount of credits first** to confirm the connection. The smallest amount you can send is 0.001 credits.
{% endhint %}

## Confirming Credit Transfer

Once your credits have been transferred, you will see the message on the screenshot below.

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FbtCMoWb9hA1GdplBy3nJ%2Ficrexportfinal.png?alt=media&amp;token=b6098599-a755-4b15-8fd3-cd131a855edc" alt=""><figcaption></figcaption></figure>

1. Click the link contained in the message to be taken to the credit inventory of your ICR organization.
2. Confirm the expected Project and Amount are showing as seen on the screenshot below.

<figure><img src="https://3780658357-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHeePsnmPrGVr0o2jKoWT%2Fuploads%2FdFYSPBXt24OyIPH61eZT%2Ficrconfirm.png?alt=media&amp;token=21eddf0d-5bf7-4392-a197-163158c317a8" alt=""><figcaption></figcaption></figure>


# Puro Bridge Integration Guide

This guide is adopted from Toucan Protocol’s reference documentation. Toucan Protocol is a third party partner that Carbonmark utilizes for interfacing with the [Puro.Earth](https://puro.earth/) registry.

Please note that ongoing updates will be made to this documentation as updates are made to Puro.Earth’s registry interface and Toucan Protocol’s credit transfer infrastructure.

### About Toucan

[Toucan](https://toucan.earth/) builds technology to accelerate climate finance. Their product suite consists of bridging, liquidity, and carbon data solutions.

### About Puro.Earth

Puro.earth certifies suppliers based on the [Puro Standard](https://puro.earth/puro-standard-carbon-removal-credits). Removal is independently verified and CO2 Removal Certificates (CORCs) are issued through the [Puro Registry](https://registry.puro.earth/carbon-sequestration/retirements). CORCs require at least 100 years of permanence.

### Toucan Puro Bridge v1

You **must have a Puro account** in order to use the bridge. A Puro platform membership gives access to the Puro Registry and allows you to purchase CORCs directly from suppliers. This membership costs €900 ([learn more](https://puro.earth/fees)) and will expand your permitted activities.

The Toucan Puro Bridge V1 enables our partners to **tokenize**, **detokenize**, and **retire** CO2 Removal Certificates (CORCs) in the Puro Registry.

Please note, some of these actions are subject to current limitations. Toucan will continue to collaborate with Puro’s engineering team to improve flexibility in the future. Should you have questions about Toucan’s technology, please `email: support@toucan.earth`.

## Import Credits to Carbonmark

Below you will find a comprehensive, step-by-step guide designed to facilitate streamlined access to CORCs on Puro.Earth’s registry.

### Step 1: Prepare Your Trader Account

If you’ve already done this, please skip to **Step 2: On-Ramp Assets**.

Each bridge user must be onboarded as a recognized “Trader” within Toucan’s Sales Channel in the Puro API. The first time you use our bridge, Toucan will first ask for your information in order to prepare for the process.

1. Go to the [Toucan Puro Bridge Trader Onboarding Form](https://toucanprotocol.typeform.com/to/NuMht1wy).
2. Fill in the necessary details, which will include information about your organization

   and a prompt to accept the bridge’s Terms and Conditions.
3. Go to **Step2: On-Ramp Assets**. In requesting that your credits are on-ramped to the Toucan Sales Channel, Puro will create your Trader account.

{% hint style="info" %}
**Important note:** Your request to create a trader account can only be initiated by Puro, and therefore requires **First Time Users** (Puro account holders who have not yet on- ramped any assets to Toucan’s Sales Channel) to be completed.
{% endhint %}

### Step 2: On-Ramp Assets

On-ramp your assets using the email template below. If you don’t yet have a Trader account in Toucan’s Sales Channel, Puro has stated that they will initiate the request to set up your Trader account when they receive this email.

#### First Time Users

For Puro account holders who **have not yet** on-ramped any assets to Toucan’s Sales Channel.

> **To:** <tech-support@puro.earth>
>
> **CC:** <support@toucan.earth>
>
> **Subject:** On-ramp assets to Toucan’s Sales Channel and initiate request to set up Trader account
>
> **Body:** Please on-ramp the following assets to Toucan’s Sales Channel and, in the process, initiate a request to set up our organization’s Trader account within the Toucan Sales Channel that Toucan can then approve:
>
> *Credit owner details*
>
> * Name of the organization (if you currently own the credits, this will be your organization)
> * Address
> * Business ID
>
> *Sales channel details*
>
> * Name of the organization: Toucan Protocol Association
> * Business ID: CHE-381.295.616
>
> *Other details*
>
> * Account number credits will be transferred **from**
> * Credit ID number (for each batch)
> * Amount of credits (for each batch)
> * **Set automatic transaction approval on**

#### Returning Users

For Puro account holders **who have already** on-ramped assets to Toucan’s Sales Channel.

> **To:** <tech-support@puro.earth>
>
> **CC:** <support@toucan.earth>
>
> **Subject:** On-ramp assets to Toucan’s Sales Channel
>
> **Body:** Please on-ramp the following assets to Toucan’s Sales Channel:
>
> *Credit owner details*
>
> * Name of the organization (if you currently own the credits, this will be your organization)
> * Address
> * Business ID
>
> *Sales channel details*
>
> * Name of the organization: Toucan Protocol Association
> * Business ID: CHE-381.295.616
>
> *Other details*
>
> * Account number credits will be transferred **from**
> * Credit ID number (for each batch)
> * Amount of credits (for each batch)
> * **Set automatic transaction approval on**

{% hint style="info" %}
**Important note for both users:** You must turn on automatic transaction approval to use the bridge.
{% endhint %}

### Step 3: Transfer Onto Carbonmark's Platform

1. **Submit Tokenization Request:** For V1, Toucan will initiate the tokenization of CORCs once you submit the [request form](https://toucanprotocol.typeform.com/to/Rg46Snpq). Later upgrades will allow partners with a Trader account to independently initiate tokenization via Toucan from Carbonmark’s UI. [This video demonstrates how to fill out the tokenization request form](https://www.loom.com/share/deaac9b07b774793ba07f1a573671b2d).
2. **Toucan Initiates Tokenization:** Upon receiving your tokenization request, Toucan will initiate the tokenization process and automatically convert your CORCs into tokenized assets.
3. **TCO2 Tokens Deposited:** The off-chain CORCs will be batch-minted into an ERC721 NFT, which will then be fractionalized into ERC20 TCO2s and deposited into the on-chain wallet that you specified in the request form (note, this essentially refers to the representation of Puro.Earth credits on our platform).

{% hint style="info" %}
**Important note:** Toucan will request an account ID (i.e. *a wallet address*) for depositing the Puro credits into your Carbonmark account. This account ID can be located from your[`Profile`](https://www.carbonmark.com/login) page on Carbonmark's platform.
{% endhint %}

## Export Credits from Carbonmark to your Puro Account

### Step 1: Submit Detokenization Request

This works in a similar manner to retirement; however, if you want to detokenize Puro Credits on Carbonmark which you didn’t previously transfer onto our platform yourself, please consult your Carbonmark point of contact before proceeding.

## Support

Should you encounter any issues or have questions, we are here to assist. The most direct channels for support are:

* Reach out to your designated Carbonmark account manager directly via email.
* Email Toucan Protocol at `support@toucan.earth` for general inquiries and technical assistance.


# Onboarding

## Overview

Carbonmark has partnered with <https://www.bridge.xyz/> (a **Stripe** company) to provide a convenient, simple, and transparent process to offramp USDC stablecoins from a Carbonmark Seller account to fiat in a bank account of the sellers choice.

## Process

{% hint style="success" %}

### Future State - Managed in Carbonmark

In 2026, Carbonmark will improve the UX for this feature by managing the process via UI interfaces within Carbonmark, leveraging the the Bridge development platform and API.
{% endhint %}

In the short term, Carbonmark will offer an OTC service and coordinate the steps with our sellers to generate payment routes (i.e. deposit addresses) which facilitate the transfer of USDC to a bank account. All of the Bridge related steps are conducted within our secure Bridge Dashboard application.

### Onboarding steps:

1. Login in to your **Carbonmark account** and navigate to the [Carbon Portfolio](https://app.carbonmark.com/portfolio) page.
2. Under `Manage Assets`, click the **OFFRAMP TO BANK** button. This will open a [Offramp to Bank Request](https://forms.zohopublic.com/carbonmark/form/OfframptoBankRequest/formperma/l1l1dm5JWpRBREjJGrBJyiUjPVn2C9QwJogKMCFkJYQ) form page.
3. Complete and **submit the form**. The Carbonmark team reviews the form information and creates a customer entry in our secure Bridge Dashboard app. We then send you an email outlining the remaining process and steps. This email includes links to the following **tasks you must complete** (needed for US and SEPA):
   1. Accept terms of service
   2. Add customer details
   3. Add source of funds
4. Once the tasks are completed successfully, your **customer status** inside the Bridge Dashboard app will be `active`.
5. The next step is to create a **Payment Route**. The Carbonmark team will work with you to collect the information required (essentially destination banking details).
6. Once the payment route is created, this will generate a **Deposit Address** that can be used in the Carbonmark app for the offramp transfer.

See [Using Deposit Address](/offramp-to-bank/using-deposit-address) page for how to use the deposit address in Carbonmark.


# Using Deposit Address

## Overview

Once you have completed the [Onboarding](/offramp-to-bank/onboarding) process, you will have received a unique **Deposit Address** (0x...1234) that is exclusively linked to your Bridge customer ID and bank payment route. You can use that Deposit Address to facilitate the USDC --> Fiat offramp from the **Carbonmark** app.

{% hint style="warning" %}
If you have questions at any time during this process, please reach out to Carbonmark for support.
{% endhint %}

## Process

1. Once the onboarding steps are complete, **Carbonmark** will send you an email with:
   1. **Deposit address** (i.e. 0x##...####).
   2. **Payment instructions PDF**; which includes the following information:
      1. `Chain` (i.e. Polygon)
      2. `Currency` (i.e. USDC)
      3. `Deposit address` (i.e. 0x##...####)
      4. Destination details (This is where the payment will land):
         1. `Destination payment rail`
         2. `Destination currency`
         3. `Bank name`
         4. `Account owner`
         5. `Account type`
         6. `Other account details`
   3. A link to this **user guide**.
2. Login in your **Carbonmark** account and navigate to your [Carbon Portfolio](https://app.carbonmark.com/portfolio) page.
3. Under `Manage Assets`, click the **TRANSFER ASSETS** button.
4. In the `Transfer Assets` modal, set the following fields:
   1. `Destination` This is the Deposit address from step #1.
   2. `Asset` USDC
   3. `Quantity` The amount of USDC to transfer to your bank account.
5. Click the **START TRANSFER..**. button and sign the transactions with your PIN number.
6. Ideally (but not required), send **Carbonmark** an email that an offramp transfer transaction has been executed. **Carbonmark** can confirm the transaction in the Bridge dashboard app.
7. Once the payment process is confirmed, the **Payment receipt** (generated from the Bridge dashboard app) can be sent to you by email.


