CarbonGuru Webhooks API
Endpoint registrations and the per-attempt delivery log.
Endpoint registrations and the per-attempt delivery log.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/carbonguru-io:carbonguru-io-webhooks-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: CarbonGuru Webhooks API
version: 0.1.0
summary: Carbon footprint analysis, history and reference data for a developer's own server.
description: 'One API over the CarbonGuru platform: carbon-footprint analysis of products, carts, text, structured items and files; the sessions, exports and history those analyses produce; reference data; offsets for the account itself; and, for an account with a connected store, catalog footprints, the live footprint read, offset ordering and webhooks. Authenticate with `Authorization: Bearer `. A test key, made in the same place, connects the same way and records every order it posts as a preview: nothing is bought. Errors are RFC 9457 problem documents. Every response carries `X-Request-ID`. Writes accept an `Idempotency-Key`. Lists page with an opaque `next_cursor`. No route accepts terms, saves a payment method or starts automatic offsetting: a signed-in person does those in the account.'
contact:
name: CarbonGuru
url: https://carbonguru.io
servers:
- url: https://carbonguru.io
description: Production
security:
- bearerAuth: []
tags:
- name: Webhooks
description: Endpoint registrations and the per-attempt delivery log.
paths:
/v1/webhook-endpoints:
get:
tags:
- Webhooks
operationId: listWebhookEndpoints
summary: The account's webhook endpoints
description: 'No secret is in this answer. A secret appears in exactly two responses: creating an endpoint and rotating its secret.'
security:
- bearerAuth:
- carbon:read
responses:
'200':
description: The registered endpoints.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/json:
schema:
type: object
required:
- endpoints
properties:
endpoints:
type: array
items:
$ref: '#/components/schemas/WebhookEndpoint'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
post:
tags:
- Webhooks
operationId: createWebhookEndpoint
summary: Register a webhook endpoint
description: 'THE SECRET IS IN THIS ANSWER ONLY. Store it when you read it: we hold a verifier for it, not the value, and no route says it again. An endpoint whose secret was lost is rotated. The URL must be https on 443 and must not resolve inside our network; a URL that is not is a 422 invalid_url, and an event we do not fire is a 422 unknown_event. A key may subscribe only to events whose scope it holds. Idempotency-Key applies to the registration and not to the secret: a replay under the same key is a 409 idempotency_secret_not_replayable.'
security:
- bearerAuth:
- carbon:read
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- url
- events
properties:
url:
type: string
description: An https endpoint on port 443.
events:
type: array
minItems: 1
items:
type: string
description: catalog.push.completed, footprint.updated, order.offset, charge.failed, settlement.completed, certificate.issued, analysis.completed, analysis.failed.
example:
url: https://shop.example/hooks/carbonguru
events:
- catalog.push.completed
- footprint.updated
responses:
'201':
description: Registered. The secret is stated once, here.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/json:
schema:
type: object
required:
- endpoint
- secret
properties:
endpoint:
$ref: '#/components/schemas/WebhookEndpoint'
secret:
type: string
description: The signing secret, whsec_… Standard Webhooks signing. It is never returned again.
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/Conflict'
'422':
$ref: '#/components/responses/UnprocessableContent'
/v1/webhook-endpoints/{endpoint_id}:
delete:
tags:
- Webhooks
operationId: deleteWebhookEndpoint
summary: Stop delivering to this endpoint
description: The endpoint's delivery log stays readable afterwards.
security:
- bearerAuth:
- carbon:read
parameters:
- $ref: '#/components/parameters/WebhookEndpointId'
responses:
'204':
description: Deleted. No body.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/v1/webhook-endpoints/{endpoint_id}/rotate-secret:
post:
tags:
- Webhooks
operationId: rotateWebhookSecret
summary: Mint a new signing secret for this endpoint
description: THE SECRET IS IN THIS ANSWER ONLY. The endpoint keeps its id, its URL and its events; deliveries after this call are signed with the new secret.
security:
- bearerAuth:
- carbon:read
parameters:
- $ref: '#/components/parameters/WebhookEndpointId'
responses:
'200':
description: The new signing secret, stated once.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/json:
schema:
type: object
required:
- secret
properties:
secret:
type: string
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/v1/webhook-deliveries:
get:
tags:
- Webhooks
operationId: listWebhookDeliveries
summary: The per-attempt delivery log
description: One row per attempt with its status code, its error and when the next attempt is due. Cursor-paged; next_cursor is opaque and the Link header carries the same cursor as rel="next".
security:
- bearerAuth:
- carbon:read
parameters:
- name: endpoint_id
in: query
required: false
schema:
type: string
- name: status
in: query
required: false
schema:
type: string
description: pending, delivered or failed.
- name: cursor
in: query
required: false
schema:
type: string
description: A next_cursor from an earlier page. Wins over limit.
- name: limit
in: query
required: false
schema:
type: integer
default: 25
minimum: 1
maximum: 100
responses:
'200':
description: A page of delivery attempts.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
Link:
description: RFC 8288. rel="next" when another page exists.
schema:
type: string
content:
application/json:
schema:
type: object
required:
- deliveries
- next_cursor
properties:
deliveries:
type: array
items:
$ref: '#/components/schemas/WebhookDelivery'
next_cursor:
type:
- string
- 'null'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'422':
$ref: '#/components/responses/UnprocessableContent'
/v1/webhook-deliveries/{delivery_id}/replay:
post:
tags:
- Webhooks
operationId: replayWebhookDelivery
summary: Send this delivery again
description: The replay is a new attempt on the same log row.
security:
- bearerAuth:
- carbon:read
parameters:
- name: delivery_id
in: path
required: true
schema:
type: string
- $ref: '#/components/parameters/IdempotencyKey'
responses:
'202':
description: The replay was accepted.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
Idempotent-Replayed:
$ref: '#/components/headers/IdempotentReplayed'
content:
application/json:
schema:
type: object
required:
- delivery
properties:
delivery:
$ref: '#/components/schemas/WebhookDelivery'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
components:
schemas:
Problem:
type: object
description: RFC 9457 problem document. Served as application/problem+json.
required:
- type
- title
- status
- detail
- code
- request_id
properties:
type:
type: string
description: A stable identifier for this problem kind.
title:
type: string
status:
type: integer
detail:
type: string
code:
type: string
description: The machine-readable name to branch on.
request_id:
type: string
errors:
type: array
description: Field-level problems, when the refusal is about the request's fields.
items:
type: object
required_scope:
type: string
description: Present on insufficient_scope.
retry_after:
type: string
description: Present on a 429, beside the Retry-After header.
max_size_bytes:
type: integer
description: Present on file_too_large.
min_tier:
type: string
description: Present on feature_not_entitled.
current_tier:
type: string
description: Present on feature_not_entitled.
upgrade_url:
type: string
description: Present on feature_not_entitled.
details:
type: object
description: Present on mapping_limit_exceeded.
WebhookDelivery:
type: object
description: 'One delivery attempt. Read it when an endpoint stopped receiving: it says what we sent, what came back, and when the next attempt is due.'
properties:
id:
type: string
endpoint_id:
type: string
event_id:
type: string
description: The event this delivery carries. Retries of one event share it.
event_type:
type: string
status:
type: string
description: pending, delivered or failed.
attempt_count:
type:
- integer
- 'null'
last_status_code:
type:
- integer
- 'null'
description: The HTTP status the endpoint answered with.
last_error:
type:
- string
- 'null'
created_at:
type:
- string
- 'null'
delivered_at:
type:
- string
- 'null'
next_attempt_at:
type:
- string
- 'null'
description: Null when no further attempt is due.
WebhookEndpoint:
type: object
description: 'One registered webhook endpoint. The signing secret is not part of the resource: it is stated once at registration and once at each rotation, and nowhere else.'
properties:
id:
type: string
url:
type: string
events:
type: array
items:
type: string
description: The events this endpoint subscribes to.
status:
type: string
description: active or disabled. An endpoint is disabled after repeated delivery failures.
created_at:
type:
- string
- 'null'
failure_count:
type:
- integer
- 'null'
description: Consecutive failed deliveries. It resets on a success.
disabled_at:
type:
- string
- 'null'
headers:
XRequestID:
description: The request id, echoed from the caller when it is a sane token, else minted. On every response and in every log line.
schema:
type: string
IdempotentReplayed:
description: Present and true when this response was stored by an earlier request under the same Idempotency-Key and replayed rather than processed again.
schema:
type: string
enum:
- 'true'
responses:
Forbidden:
description: A missing scope, an exhausted analysis allowance, or an object that is not the caller's.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
NotFound:
description: No such resource.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
Conflict:
description: An Idempotency-Key was reused with a different body (idempotency_key_reuse), or a request under the same key is still running (idempotency_in_progress).
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
Unauthorized:
description: No valid credential. Every authentication failure returns this one document.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
UnprocessableContent:
description: The request's fields are wrong. errors[] names them.
headers:
X-Request-ID:
$ref: '#/components/headers/XRequestID'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
parameters:
WebhookEndpointId:
name: endpoint_id
in: path
required: true
schema:
type: string
description: The endpoint id the registration returned.
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
schema:
type: string
minLength: 1
maxLength: 255
description: 'A key of the caller''s choosing, held 24 hours and scoped to the account, the method and the path. The same key with the same body replays the stored response with Idempotent-Replayed: true. The same key with a different body is a 409 idempotency_key_reuse. A request still running under the key is a 409 idempotency_in_progress. A key longer than 255 characters or holding control characters is a 422. A write whose record cannot be stored is still processed once, without the replay guarantee. A response carrying a secret is never replayed: the second request receives a 409 idempotency_secret_not_replayable.'
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: An API key from /my-account/api/ (`sk_merchant_…`), or a CarbonGuru OAuth access token. A test key (`sk_merchant_test_…`) connects like a live key; every order it posts is recorded as a preview and nothing is bought. Auth runs before body validation, and every failure returns the same 401.