Skip to main content
POST
/
api
/
v1
/
enterprise
/
eudr
/
webhooks
Python
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)
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);
}
}
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());
}
}
}
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);
}
}
}
curl --request POST \
--url https://live.agribackup.com/api/v1/enterprise/eudr/webhooks \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--header 'X-API-Key: <api-key>' \
--data '
{
"targetUrl": "https://erp.example.com/agribackup-webhooks",
"eventTypes": [
"shipment.linked",
"polygon.verified",
"batch.risk_assessed",
"dds.submitted",
"dds.validated",
"dds.rejected",
"job.failed",
"report.ready"
]
}
'
const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
'X-API-Key': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
targetUrl: 'https://erp.example.com/agribackup-webhooks',
eventTypes: [
'shipment.linked',
'polygon.verified',
'batch.risk_assessed',
'dds.submitted',
'dds.validated',
'dds.rejected',
'job.failed',
'report.ready'
]
})
};

fetch('https://live.agribackup.com/api/v1/enterprise/eudr/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://live.agribackup.com/api/v1/enterprise/eudr/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'targetUrl' => 'https://erp.example.com/agribackup-webhooks',
'eventTypes' => [
'shipment.linked',
'polygon.verified',
'batch.risk_assessed',
'dds.submitted',
'dds.validated',
'dds.rejected',
'job.failed',
'report.ready'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>",
"X-API-Key: <api-key>"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://live.agribackup.com/api/v1/enterprise/eudr/webhooks"

payload := strings.NewReader("{\n \"targetUrl\": \"https://erp.example.com/agribackup-webhooks\",\n \"eventTypes\": [\n \"shipment.linked\",\n \"polygon.verified\",\n \"batch.risk_assessed\",\n \"dds.submitted\",\n \"dds.validated\",\n \"dds.rejected\",\n \"job.failed\",\n \"report.ready\"\n ]\n}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("Idempotency-Key", "<idempotency-key>")
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
require 'uri'
require 'net/http'

url = URI("https://live.agribackup.com/api/v1/enterprise/eudr/webhooks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"targetUrl\": \"https://erp.example.com/agribackup-webhooks\",\n \"eventTypes\": [\n \"shipment.linked\",\n \"polygon.verified\",\n \"batch.risk_assessed\",\n \"dds.submitted\",\n \"dds.validated\",\n \"dds.rejected\",\n \"job.failed\",\n \"report.ready\"\n ]\n}"

response = http.request(request)
puts response.read_body
{
  "webhookId": "wh_12345",
  "status": "REGISTERED",
  "targetUrl": "https://erp.example.com/agribackup-webhooks",
  "signingSecret": "whsec_0123456789abcdef"
}
{
"code": "VALIDATION_FAILED",
"message": "Invalid polygon coordinates",
"details": [
{
"code": "INVALID_COORDINATE_CLOSURE",
"field": "features[0].properties.area",
"issue": "Area must be specified in hectares and cannot be negative"
}
]
}
{
"code": "VALIDATION_FAILED",
"message": "Invalid polygon coordinates",
"details": [
{
"code": "INVALID_COORDINATE_CLOSURE",
"field": "features[0].properties.area",
"issue": "Area must be specified in hectares and cannot be negative"
}
]
}
{
"code": "VALIDATION_FAILED",
"message": "Invalid polygon coordinates",
"details": [
{
"code": "INVALID_COORDINATE_CLOSURE",
"field": "features[0].properties.area",
"issue": "Area must be specified in hectares and cannot be negative"
}
]
}
{
"code": "VALIDATION_FAILED",
"message": "Invalid polygon coordinates",
"details": [
{
"code": "INVALID_COORDINATE_CLOSURE",
"field": "features[0].properties.area",
"issue": "Area must be specified in hectares and cannot be negative"
}
]
}
{
"code": "VALIDATION_FAILED",
"message": "Invalid polygon coordinates",
"details": [
{
"code": "INVALID_COORDINATE_CLOSURE",
"field": "features[0].properties.area",
"issue": "Area must be specified in hectares and cannot be negative"
}
]
}
{
"code": "VALIDATION_FAILED",
"message": "Invalid polygon coordinates",
"details": [
{
"code": "INVALID_COORDINATE_CLOSURE",
"field": "features[0].properties.area",
"issue": "Area must be specified in hectares and cannot be negative"
}
]
}
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.
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 endpoint to generate a new one.

Supported Event Types

Event TypeTrigger
shipment.linkedA batch has been cryptographically anchored to a logistics shipment
polygon.verifiedA polygon ingestion and satellite screening job completed
batch.risk_assessedCopernicus NDVI risk assessment completed for a batch
dds.submittedA Due Diligence Statement was successfully submitted to TRACES NT
dds.validatedTRACES NT validated and cleared the DDS at border
dds.rejectedTRACES NT rejected the DDS (e.g., mismatched Operator UUIDs)
job.failedAn asynchronous compliance job terminated with an unrecoverable error
report.readyA 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.
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)
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)
  );
}
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"]
  }'

Authorizations

X-API-Key
string
header
required

Enterprise API Key provided via the AgriBackup developer console.

Headers

Idempotency-Key
string<uuid>
required

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.

Body

application/json

Payload to register a webhook listener

targetUrl
string<uri>
required

The enterprise endpoint URL to receive POST events

Pattern: ^https://.*
Example:

"https://erp.example.com/agribackup-webhooks"

eventTypes
enum<string>[]
required

Event type to subscribe to

Available options:
shipment.linked,
polygon.verified,
batch.risk_assessed,
dds.submitted,
dds.validated,
dds.rejected,
job.failed,
report.ready
Example:
[
"shipment.linked",
"polygon.verified",
"batch.risk_assessed",
"dds.submitted",
"dds.validated",
"dds.rejected",
"job.failed",
"report.ready"
]

Callbacks

POST
{$request.body#/targetUrl}shipment.linked

Body

application/json

Payload sent to registered endpoints when a shipment is linked to a batch

eventType
string
required

The type of the event

Example:

"shipment.linked"

eventId
string
required

Event unique ID

Example:

"evt_9988776655"

timestamp
string<date-time>
required

Timestamp of the event

Example:

"2026-06-12T12:00:00Z"

attempt
integer<int32>
required

The retry attempt number. 0 for the original broadcast.

Example:

0

data
object
required

Shipment Linked Data

nextRetry
string<date-time>

The scheduled timestamp for the next retry attempt if this delivery fails

Example:

"2026-06-12T12:05:00Z"

Response

200

Successfully processed by enterprise

POST
{$request.body#/targetUrl}polygon.verified

Body

application/json

Payload sent to registered endpoints when a polygon ingestion job completes

eventType
string
required

The type of the event

Example:

"polygon.verified"

eventId
string
required

Event unique ID

Example:

"evt_1122334455"

timestamp
string<date-time>
required

Timestamp of the event

Example:

"2026-06-12T12:00:00Z"

attempt
integer<int32>
required

The retry attempt number. 0 for the original broadcast.

Example:

0

data
object
required

Polygon Verified Data

nextRetry
string<date-time>

The scheduled timestamp for the next retry attempt if this delivery fails

Example:

"2026-06-12T12:05:00Z"

Response

200

Successfully processed by enterprise

POST
{$request.body#/targetUrl}batch.risk_assessed

Response

200

Successfully processed by enterprise

POST
{$request.body#/targetUrl}dds.submitted

Response

200

Successfully processed by enterprise

POST
{$request.body#/targetUrl}dds.validated

Response

200

Successfully processed by enterprise

POST
{$request.body#/targetUrl}dds.rejected

Response

200

Successfully processed by enterprise

POST
{$request.body#/targetUrl}job.failed

Response

200

Successfully processed by enterprise

POST
{$request.body#/targetUrl}report.ready

Response

200

Successfully processed by enterprise

Response

Webhook successfully registered

Response payload after registering a webhook

webhookId
string
required

The unique identifier of the webhook registration

Example:

"wh_12345"

status
string
required

Status of registration

Example:

"REGISTERED"

targetUrl
string
required

The registered callback target URL

Example:

"https://erp.example.com/agribackup-webhooks"

signingSecret
string
required

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"