> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agribackup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Register a Webhook Endpoint for EUDR Compliance Events

> Register an enterprise HTTPS URL to receive signed, asynchronous EUDR compliance events such as shipment.linked, polygon.verified, and dds.validated.

Registers an enterprise HTTPS endpoint to receive asynchronous EUDR compliance event notifications. AgriBackup signs every outbound payload using HMAC-SHA256 and includes the hex-encoded signature in the `X-AgriBackup-Signature` request header so your server can verify authenticity before processing.

<Warning>
  The `signingSecret` field in the response is returned **only once** at registration time and cannot be retrieved again. Store it immediately in a secrets manager (e.g., HashiCorp Vault, AWS Secrets Manager). If the secret is lost, use the [Rotate Webhook Secret](/sdks/enterprise-webhooks/rotate-webhook-secret) endpoint to generate a new one.
</Warning>

## Supported Event Types

| Event Type            | Trigger                                                               |
| --------------------- | --------------------------------------------------------------------- |
| `shipment.linked`     | A batch has been cryptographically anchored to a logistics shipment   |
| `polygon.verified`    | A polygon ingestion and satellite screening job completed             |
| `batch.risk_assessed` | Copernicus NDVI risk assessment completed for a batch                 |
| `dds.submitted`       | A Due Diligence Statement was successfully submitted to TRACES NT     |
| `dds.validated`       | TRACES NT validated and cleared the DDS at border                     |
| `dds.rejected`        | TRACES NT rejected the DDS (e.g., mismatched Operator UUIDs)          |
| `job.failed`          | An asynchronous compliance job terminated with an unrecoverable error |
| `report.ready`        | A bulk archival report generation job completed successfully          |

## Verifying Webhook Signatures

To confirm a webhook payload originated from AgriBackup and was not tampered with in transit:

1. Read the raw request body as bytes — do **not** parse or re-serialise before hashing.
2. Extract the signature from the `X-AgriBackup-Signature` HTTP header.
3. Compute `HMAC-SHA256(signingSecret, rawBody)` and hex-encode the result.
4. Use a **constant-time comparison** function to compare your computed hash with the header value. Avoid standard string equality to prevent timing attacks.

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_signature(raw_body: bytes, secret: str, header_sig: str) -> bool:
      computed = hmac.new(
          secret.encode("utf-8"),
          raw_body,
          hashlib.sha256
      ).hexdigest()
      return hmac.compare_digest(computed, header_sig)
  ```

  ```javascript Node.js theme={null}
  const crypto = require("crypto");

  function verifySignature(rawBody, secret, headerSig) {
    const computed = crypto
      .createHmac("sha256", secret)
      .update(rawBody)
      .digest("hex");
    return crypto.timingSafeEqual(
      Buffer.from(computed),
      Buffer.from(headerSig)
    );
  }
  ```

  ```bash cURL theme={null}
  curl -X POST https://live.agribackup.com/api/v1/enterprise/eudr/webhooks \
    -H "X-API-Key: sk_live_YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
    -d '{
      "targetUrl": "https://erp.example.com/agribackup-webhooks",
      "eventTypes": ["shipment.linked", "polygon.verified", "dds.validated"]
    }'
  ```
</CodeGroup>


## OpenAPI

````yaml post /api/v1/enterprise/eudr/webhooks
openapi: 3.0.1
info:
  title: AgriBackup Enterprise EUDR API
  description: >-
    # Introduction


    AgriBackup is the cryptographic compliance engine for EU-bound commodity
    imports. Built on Deterministic Logic, Immutable Evidence, and
    High-Throughput Ingestion, our API provides Tier-1 enterprise routing for EU
    Deforestation Regulation (EUDR).


    ## The Integration Vector

    To integrate this Digital Trust Layer into your ERP, you must execute the
    following sequence:

    1. **Authentication:** Generate your `X-API-Key` from the client dashboard.

    2. **Telemetry:** Register your webhook URLs via `POST /webhooks`.

    3. **Ingestion:** Push your GeoJSON polygons via `POST /polygons`.

    4. **Logistics:** Lock the batch to a shipment via `POST /shipments/link`.


    ## Asynchronous Architecture

    Because satellite deforestation screening and Hedera DLT anchoring take
    time, ingestion endpoints return a `202 Accepted`. You must listen for the
    corresponding Webhook to confirm cryptographic execution.


    ## Base URLs

    * **Production:** `https://live.agribackup.com`

    * **Sandbox:** `https://sandbox.agribackup.com`
  contact:
    name: AgriBackup Architecture Team
    url: https://agribackup.com
    email: contact@agribackup.com
  license:
    name: Proprietary
    url: https://agribackup.com/terms
  version: v1.0
servers:
  - url: https://live.agribackup.com
    description: Production (Mainnet)
  - url: https://sandbox.agribackup.com
    description: Sandbox (Testnet)
security: []
tags:
  - name: Enterprise Risk Management
  - name: Enterprise Clearance Webhooks
    description: Asynchronous status receivers for TRACES NT
  - name: Enterprise Jobs
    description: Query status and metrics of asynchronous compliance jobs
  - name: Enterprise Suppliers
  - name: Enterprise Diagnostics
    description: Monitor API health, consensus ledger, and satellite systems availability
  - name: Enterprise Suppliers
    description: Map internal ERP vendor IDs to EU Operator UUIDs
  - name: Enterprise Webhooks
    description: Register endpoints for asynchronous compliance events
  - name: Enterprise Archival
    description: Endpoints for bulk compliance archiving and auditor extensibility
  - name: Enterprise Logistics
    description: Smart contract linking of batches to logistics shipments
  - name: Enterprise Declarations
    description: DDS generation with legal land tenure proofs
  - name: Enterprise Batches
  - name: Enterprise API Keys
  - name: Enterprise Diagnostics
  - name: Enterprise Logistics
  - name: Enterprise Jobs
  - name: Enterprise Evidence
    description: Cryptographic evidence ledger anchored on Hedera Hashgraph
  - name: Enterprise Declarations
  - name: Enterprise Credentials
  - name: Enterprise Evidence
  - name: Enterprise Risk Management
    description: Pre-assessment of country risk and satellite deforestation probabilities
  - name: Enterprise Batches
    description: Programmatic batch ingestion and management
  - name: Enterprise Polygons
  - name: Introduction
  - name: Enterprise Polygons
    description: Programmatic polygon ingestion and verification
  - name: Enterprise Archival
  - name: Enterprise Credentials
    description: Programmatic ingestion of vault-secured enterprise secrets
  - name: Enterprise API Keys
    description: Endpoints for managing B2B API Keys (Requires standard JWT login)
  - name: Enterprise Webhooks
paths:
  /api/v1/enterprise/eudr/webhooks:
    post:
      tags:
        - Enterprise Webhooks
      summary: Register Webhook Endpoint
      description: >-
        Registers an enterprise URL to receive asynchronous compliance events
        like shipment.linked and polygon.verified. Webhook payloads are
        cryptographically signed using HMAC-SHA256 with the generated signing
        secret, and sent in the 'X-AgriBackup-Signature' header for
        verification.


        To verify webhook authenticity:

        1. Extract the raw POST request body bytes/string.

        2. Extract the signature value from the 'X-AgriBackup-Signature' HTTP
        header.

        3. Calculate the HMAC-SHA256 hash of the raw body using the registered
        signing secret.

        4. Hex-encode the calculated hash.

        5. Perform a constant-time comparison between the hex-encoded hash and
        the header signature to avoid timing attacks.
      operationId: registerWebhook
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookRegistrationRequest'
        required: true
      responses:
        '200':
          description: Webhook successfully registered
          headers:
            X-RateLimit-Remaining:
              description: >-
                The number of requests remaining in the current rate limit
                window.
              schema:
                type: integer
                format: int32
            X-RateLimit-Reset:
              description: >-
                The time at which the current rate limit window resets in UTC
                epoch seconds.
              schema:
                type: integer
                format: int32
            X-RateLimit-Limit:
              description: >-
                The maximum number of requests you're permitted to make per
                hour.
              schema:
                type: integer
                format: int32
            X-Trace-Id:
              description: Unique request trace identifier
              style: simple
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookRegistrationResponse'
        '400':
          description: Bad Request (e.g. invalid URL format or unsupported event type)
          headers:
            X-RateLimit-Limit:
              description: >-
                The maximum number of requests you're permitted to make per
                hour.
              schema:
                type: integer
                format: int32
            X-RateLimit-Remaining:
              description: >-
                The number of requests remaining in the current rate limit
                window.
              schema:
                type: integer
                format: int32
            X-RateLimit-Reset:
              description: >-
                The time at which the current rate limit window resets in UTC
                epoch seconds.
              schema:
                type: integer
                format: int32
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '401':
          description: Unauthorized
          headers:
            X-RateLimit-Limit:
              description: >-
                The maximum number of requests you're permitted to make per
                hour.
              schema:
                type: integer
                format: int32
            X-RateLimit-Remaining:
              description: >-
                The number of requests remaining in the current rate limit
                window.
              schema:
                type: integer
                format: int32
            X-RateLimit-Reset:
              description: >-
                The time at which the current rate limit window resets in UTC
                epoch seconds.
              schema:
                type: integer
                format: int32
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '403':
          description: Forbidden
          headers:
            X-RateLimit-Limit:
              description: >-
                The maximum number of requests you're permitted to make per
                hour.
              schema:
                type: integer
                format: int32
            X-RateLimit-Remaining:
              description: >-
                The number of requests remaining in the current rate limit
                window.
              schema:
                type: integer
                format: int32
            X-RateLimit-Reset:
              description: >-
                The time at which the current rate limit window resets in UTC
                epoch seconds.
              schema:
                type: integer
                format: int32
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '409':
          description: Conflict (Webhook for this event and URL already exists)
          headers:
            X-RateLimit-Limit:
              description: >-
                The maximum number of requests you're permitted to make per
                hour.
              schema:
                type: integer
                format: int32
            X-RateLimit-Remaining:
              description: >-
                The number of requests remaining in the current rate limit
                window.
              schema:
                type: integer
                format: int32
            X-RateLimit-Reset:
              description: >-
                The time at which the current rate limit window resets in UTC
                epoch seconds.
              schema:
                type: integer
                format: int32
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '423':
          description: >-
            Locked: Execution thread is currently processing the resource. Wait
            and retry.
          headers:
            X-RateLimit-Limit:
              description: >-
                The maximum number of requests you're permitted to make per
                hour.
              schema:
                type: integer
                format: int32
            X-RateLimit-Remaining:
              description: >-
                The number of requests remaining in the current rate limit
                window.
              schema:
                type: integer
                format: int32
            X-RateLimit-Reset:
              description: >-
                The time at which the current rate limit window resets in UTC
                epoch seconds.
              schema:
                type: integer
                format: int32
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '429':
          description: Too Many Requests
          headers:
            X-RateLimit-Limit:
              description: >-
                The maximum number of requests you're permitted to make per
                hour.
              schema:
                type: integer
                format: int32
            X-RateLimit-Remaining:
              description: >-
                The number of requests remaining in the current rate limit
                window.
              schema:
                type: integer
                format: int32
            X-RateLimit-Reset:
              description: >-
                The time at which the current rate limit window resets in UTC
                epoch seconds.
              schema:
                type: integer
                format: int32
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
      callbacks:
        shipment.linked:
          '{$request.body#/targetUrl}':
            post:
              summary: Shipment Linked Event
              description: Fired when a batch shipment is successfully anchored.
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              requestBody:
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/WebhookShipmentLinkedPayload'
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
        polygon.verified:
          '{$request.body#/targetUrl}':
            post:
              summary: Polygon Verified Event
              description: Fired when a polygon ingestion job completes.
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              requestBody:
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/WebhookPolygonVerifiedPayload'
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
        batch.risk_assessed:
          '{$request.body#/targetUrl}':
            post:
              summary: Batch Risk Assessed Event
              description: >-
                Fired when Copernicus NDVI risk assessment completes for a
                batch.
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
        dds.submitted:
          '{$request.body#/targetUrl}':
            post:
              summary: DDS Submitted Event
              description: >-
                Fired when the TRACES NT handshake succeeds and places the
                payload in the queue.
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
        dds.validated:
          '{$request.body#/targetUrl}':
            post:
              summary: DDS Validated Event
              description: >-
                Fired asynchronously by TRACES NT when the DDS is fully
                validated and clears border checks.
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
        dds.rejected:
          '{$request.body#/targetUrl}':
            post:
              summary: DDS Rejected Event
              description: >-
                Fired asynchronously by TRACES NT if the payload is rejected due
                to internal cross-checks (e.g., mismatched Operator UUIDs).
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
        job.failed:
          '{$request.body#/targetUrl}':
            post:
              summary: Job Failed Event
              description: Critical for alerting the ERP if an asynchronous thread crashes.
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
        report.ready:
          '{$request.body#/targetUrl}':
            post:
              summary: Bulk Archival Report Ready Event
              description: >-
                Fired when an asynchronous bulk report generation job completes
                successfully.
              parameters:
                - name: X-AgriBackup-Signature
                  in: header
                  required: true
                  schema:
                    type: string
              responses:
                '200':
                  description: Successfully processed by enterprise
              method: post
              type: path
            path: '{$request.body#/targetUrl}'
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: Python
          source: >-
            import agribackup

            from agribackup.rest import ApiException


            # Initialize Client

            configuration =
            agribackup.Configuration(host="https://live.agribackup.com/api/v1")

            configuration.api_key['ApiKeyAuth'] = 'sk_live_YOUR_API_KEY'


            with agribackup.ApiClient(configuration) as api_client:
                try:
                    api_instance = agribackup.EnterpriseWebhooksApi(api_client)
                    response = api_instance.register_webhook()
                    print(response)
                except ApiException as e:
                    print("Exception when calling API: %s\n" % e)
        - lang: Node.js
          source: |-
            import { AgriBackupClient } from '@agribackup/sdk';

            // Initialize Client
            const client = new AgriBackupClient({
              apiKey: 'sk_live_YOUR_API_KEY',
              baseUrl: 'https://live.agribackup.com/api/v1'
            });

            async function execute() {
              try {
                const response = await client.webhooks.registerWebhook();
                console.log(response);
              } catch (error) {
                console.error(error);
              }
            }
        - lang: Java
          source: |-
            import com.agribackup.client.*;
            import com.agribackup.client.api.*;
            import com.agribackup.client.model.*;

            public class Example {
                public static void main(String[] args) {
                    ApiClient defaultClient = Configuration.getDefaultApiClient();
                    defaultClient.setBasePath("https://live.agribackup.com/api/v1");
                    defaultClient.setApiKey("sk_live_YOUR_API_KEY");

                    EnterpriseWebhooksApi apiInstance = new EnterpriseWebhooksApi(defaultClient);
                    try {
                        Object result = apiInstance.registerWebhook();
                        System.out.println(result);
                    } catch (ApiException e) {
                        System.err.println("Exception when calling API: " + e.getResponseBody());
                    }
                }
            }
        - lang: C#
          source: |-
            using System;
            using System.Threading.Tasks;
            using AgriBackup.SDK.Api;
            using AgriBackup.SDK.Client;
            using AgriBackup.SDK.Model;

            class Program {
                static async Task Main() {
                    Configuration config = new Configuration();
                    config.BasePath = "https://live.agribackup.com/api/v1";
                    config.AddApiKey("ApiKeyAuth", "sk_live_YOUR_API_KEY");

                    var apiInstance = new EnterpriseWebhooksApi(config);
                    try {
                        var result = await apiInstance.RegisterWebhookAsync();
                        Console.WriteLine(result);
                    } catch (ApiException e) {
                        Console.WriteLine("Exception when calling API: " + e.Message);
                    }
                }
            }
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: >-
        Unique idempotency key (UUID) to prevent duplicate operations. Safely
        stored in Redis with a strict 24-hour TTL to prevent memory bloat during
        saturation attacks.
      required: true
      schema:
        type: string
        format: uuid
  schemas:
    WebhookRegistrationRequest:
      required:
        - eventTypes
        - targetUrl
      type: object
      properties:
        targetUrl:
          pattern: ^https://.*
          type: string
          description: The enterprise endpoint URL to receive POST events
          format: uri
          example: https://erp.example.com/agribackup-webhooks
        eventTypes:
          type: array
          example:
            - shipment.linked
            - polygon.verified
            - batch.risk_assessed
            - dds.submitted
            - dds.validated
            - dds.rejected
            - job.failed
            - report.ready
          items:
            type: string
            description: Event type to subscribe to
            enum:
              - shipment.linked
              - polygon.verified
              - batch.risk_assessed
              - dds.submitted
              - dds.validated
              - dds.rejected
              - job.failed
              - report.ready
      description: Payload to register a webhook listener
    WebhookRegistrationResponse:
      required:
        - signingSecret
        - status
        - targetUrl
        - webhookId
      type: object
      properties:
        webhookId:
          type: string
          description: The unique identifier of the webhook registration
          example: wh_12345
        status:
          type: string
          description: Status of registration
          example: REGISTERED
        targetUrl:
          type: string
          description: The registered callback target URL
          example: https://erp.example.com/agribackup-webhooks
        signingSecret:
          type: string
          description: >-
            Cryptographic signing secret used to verify webhook payloads.
            Payloads sent to your callback URL are signed with HMAC-SHA256 using
            this secret and included in the 'X-AgriBackup-Signature' header.
            Verify by hashing the raw request body with this secret and
            comparing with the header value.
          example: whsec_0123456789abcdef
      description: Response payload after registering a webhook
    ApiError:
      required:
        - code
        - details
        - message
      type: object
      properties:
        code:
          type: string
          description: Error code
          example: VALIDATION_FAILED
        message:
          type: string
          description: Human-readable error message
          example: Invalid polygon coordinates
        details:
          type: array
          description: Detailed list of validation errors, if applicable
          items:
            $ref: '#/components/schemas/ApiErrorDetail'
      description: Standard error format for all Enterprise API failures
    WebhookShipmentLinkedPayload:
      required:
        - attempt
        - data
        - eventId
        - eventType
        - timestamp
      type: object
      properties:
        eventType:
          type: string
          description: The type of the event
          example: shipment.linked
        eventId:
          type: string
          description: Event unique ID
          example: evt_9988776655
        timestamp:
          type: string
          description: Timestamp of the event
          format: date-time
          example: '2026-06-12T12:00:00Z'
        attempt:
          type: integer
          description: The retry attempt number. 0 for the original broadcast.
          format: int32
          example: 0
        nextRetry:
          type: string
          description: >-
            The scheduled timestamp for the next retry attempt if this delivery
            fails
          format: date-time
          example: '2026-06-12T12:05:00Z'
        data:
          $ref: '#/components/schemas/ShipmentLinkedData'
      description: >-
        Payload sent to registered endpoints when a shipment is linked to a
        batch
    WebhookPolygonVerifiedPayload:
      required:
        - attempt
        - data
        - eventId
        - eventType
        - timestamp
      type: object
      properties:
        eventType:
          type: string
          description: The type of the event
          example: polygon.verified
        eventId:
          type: string
          description: Event unique ID
          example: evt_1122334455
        timestamp:
          type: string
          description: Timestamp of the event
          format: date-time
          example: '2026-06-12T12:00:00Z'
        attempt:
          type: integer
          description: The retry attempt number. 0 for the original broadcast.
          format: int32
          example: 0
        nextRetry:
          type: string
          description: >-
            The scheduled timestamp for the next retry attempt if this delivery
            fails
          format: date-time
          example: '2026-06-12T12:05:00Z'
        data:
          $ref: '#/components/schemas/PolygonVerifiedData'
      description: >-
        Payload sent to registered endpoints when a polygon ingestion job
        completes
    ApiErrorDetail:
      required:
        - code
        - field
        - issue
      type: object
      properties:
        code:
          type: string
          description: Programmatic error code for automated branch logic
          example: INVALID_COORDINATE_CLOSURE
        field:
          type: string
          description: The field or property that caused the error
          example: features[0].properties.area
        issue:
          type: string
          description: A description of the issue with the field
          example: Area must be specified in hectares and cannot be negative
      description: Detailed error information for a specific field
    ShipmentLinkedData:
      required:
        - batchId
        - shipmentReference
        - transactionHash
      type: object
      properties:
        batchId:
          type: string
          description: The batch ID
          example: batch_12345
        shipmentReference:
          type: string
          description: The shipment reference
          example: SHIP-2026-001
        transactionHash:
          type: string
          description: Hedera transaction ID or hash
          example: 0.0.12345@1234567890.000000000
      description: Shipment Linked Data
    PolygonVerifiedData:
      required:
        - jobId
        - polygonIds
        - polygonsFailed
        - polygonsVerified
        - status
      type: object
      properties:
        jobId:
          type: string
          description: The ingestion job ID
          example: job_998877
        polygonsVerified:
          type: integer
          description: Number of polygons successfully verified
          format: int32
          example: 150
        polygonsFailed:
          type: integer
          description: Number of polygons that failed verification
          format: int32
          example: 3
        status:
          type: string
          description: Overall status
          example: COMPLETED
        polygonIds:
          type: array
          description: Array of successfully generated unique Polygon IDs
          items:
            type: string
            description: Array of successfully generated unique Polygon IDs
      description: Polygon Verified Data
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      description: Enterprise API Key provided via the AgriBackup developer console.
      name: X-API-Key
      in: header

````