Skip to content

Webhooks ​

Τα webhooks του CourierHub API v2 σας ειδοποιούν αμέσως όταν δημιουργείται, αλλάζει ή διαγράφεται μια αποστολή, χωρίς να χρειάζεται να ρωτάτε συνεχώς το API (polling). Το CourierHub στέλνει ένα POST με JSON στο URL που έχετε δηλώσει και εσείς απαντάτε με 2xx.

Διαφορετικό από τα webhooks των e-shop

Δεν έχει σχέση με τα webhooks που στέλνουν τα e-shop προς το CourierHub (νέες παραγγελίες). Εδώ το CourierHub είναι ο αποστολέας και το δικό σας σύστημα ο παραλήπτης. Τα endpoints δηλώνονται με το API διαχείρισης webhook endpoints ή από Ρυθμίσεις > Webhooks.

Γεγονότα ​

ΓεγονόςΠότε στέλνεται
shipment.createdΕκδόθηκε νέο voucher (δημιουργήθηκε αποστολή). Στέλνεται αμέσως.
shipment.updatedΥπάρχουν νέα tracking events ή/και άλλαξε η κατάσταση (status) της αποστολής.
shipment.deletedΔιαγράφηκε η αποστολή (ή η παραγγελία της).
pingΔοκιμαστικό γεγονός, μόνο όταν το ζητήσετε (δοκιμή). Δεν χρειάζεται εγγραφή σε αυτό.

Κάθε endpoint λαμβάνει όσα γεγονότα έχετε επιλέξει στο events. Με events: null λαμβάνει όλα.

Σημειώσεις:

  • Το shipment.updated στέλνεται ένα ανά εκτέλεση tracking: όταν η courier φέρνει πολλά νέα events μαζί, παίρνετε ένα webhook με όλα στο new_events, όχι ένα ανά event. Στέλνεται περίπου 1 λεπτό μετά την καταχώριση των events, ώστε να συγκεντρωθούν.
  • Το shipment.updated περιέχει κάθε αλλαγή που δεν έχει ακόμα παραδοθεί σε αυτό το endpoint. Αν ένα webhook αποτύχει οριστικά (και οι 8 προσπάθειες, μη επιτρεπτό URL ή ανενεργό endpoint), τα events και η αλλαγή κατάστασης που μετέφερε ξαναστέλνονται με το επόμενο webhook της ίδιας αποστολής. Ένα event μπορεί έτσι να φτάσει σε περισσότερα από ένα webhooks (με διαφορετικό CourierHub-Delivery). Βλ. Σειρά και διπλότυπα.
  • Όσο ένα endpoint είναι ανενεργό δεν μπαίνει τίποτα σε αναμονή για αυτό. Μετά την επανενεργοποίηση ξαναστέλνονται μόνο τα events των webhooks που είχαν αποτύχει, μαζί με την επόμενη αλλαγή. Το data.shipment δείχνει πάντα την τρέχουσα κατάσταση.
  • Ως "αποστολή" μιας παραγγελίας θεωρείται το τρέχον voucher της (το νεότερο ενεργό voucher). Αν η παραγγελία έχει προωθηθεί σε άλλη courier και έχει πολλά vouchers, τα webhooks ακολουθούν το νεότερο. Τα new_events περιέχουν μόνο events της courier του voucher.
  • Μετά από μακρά διακοπή του συστήματός σας μπορείτε να συγχρονίσετε το ιστορικό με το GET /shipments/{voucher}/tracking.

Περιεχόμενο αποστολής ​

Κάθε αποστολή έχει την ίδια δομή (envelope):

Variable NameTypeDescription
idStringUUID του γεγονότος. Ίδιο με την κεφαλίδα CourierHub-Delivery και ίδιο σε όλες τις επαναλήψεις της ίδιας αποστολής.
typeStringshipment.created, shipment.updated, shipment.deleted ή ping.
created_atStringΠότε δημιουργήθηκε το γεγονός (ISO 8601, UTC, Z).
data.shipmentObjectΗ αποστολή, με τα ίδια πεδία με το GET /shipments/{voucher}.
data.previous_statusString ή nullΗ κατάσταση πριν από την πρώτη (παλαιότερη) αλλαγή που περιέχεται στο webhook. Μετά από αποτυχία μπορεί να είναι παλαιότερη από την τελευταία κατάσταση που λάβατε. Αλλιώς, η κατάσταση του τελευταίου webhook που δημιουργήθηκε για την αποστολή σε αυτό το endpoint, ακόμα κι αν εκκρεμεί ή απέτυχε (μετράνε όλα τα webhooks, όχι μόνο όσα παραδόθηκαν με επιτυχία). null αν δεν έχει δημιουργηθεί ξανά webhook (και πάντα null στο shipment.created).
data.new_eventsArrayΤα tracking events που δεν έχουν παραδοθεί ακόμα, παλαιότερο πρώτο, με τα πεδία του tracking (occurred_at, description, details, code).

Το ping έχει διαφορετικό data: {endpoint_id, message}.

Σημείωση για τα παραδείγματα

Οι αποστολές στέλνονται ως συμπαγές JSON σε μία γραμμή. Τα παραδείγματα εδώ είναι μορφοποιημένα για ευκολία ανάγνωσης. Τα ονόματα πεδίων, οι τύποι και η σειρά είναι ακριβώς όπως στέλνονται.

shipment.created ​

Νέο voucher. Συνήθως δεν υπάρχουν ακόμα tracking events (new_events: []).

json
{
    "id": "fa30d8c8-b01c-45cc-8ade-e6732c0b0cbc",
    "type": "shipment.created",
    "created_at": "2026-10-14T07:05:00Z",
    "data": {
        "shipment": {
            "voucher_number": "7012345678",
            "courier": {
                "id": 8,
                "name": "ACS"
            },
            "status": "created",
            "status_label": "Δημιουργήθηκε",
            "delivered_at": null,
            "returned_at": null,
            "cod_amount": "49.90",
            "cod_paid": false,
            "last_event": null,
            "tracking_url": "https://courierhub.gr/t/mQ7CdcKy",
            "created_at": "2026-10-14T07:05:00Z",
            "order": {
                "id": 1,
                "order_id": "5012345678901",
                "order_number": "#12345",
                "store": {
                    "id": 1,
                    "name": "Το Κατάστημά μου"
                }
            }
        },
        "previous_status": null,
        "new_events": []
    }
}

shipment.updated ​

Η αποστολή παραλήφθηκε από την courier (previous_status: "created", νέα κατάσταση in_transit) και ήρθαν τρία events μαζί. Παρατηρήστε τις τρεις μορφές του details: null, αντικείμενο (με κενό αντικείμενο {} που διατηρείται) και string:

json
{
    "id": "32fbcf89-41c8-4c1b-b87d-c84220e1a272",
    "type": "shipment.updated",
    "created_at": "2026-10-14T07:05:00Z",
    "data": {
        "shipment": {
            "voucher_number": "7012345678",
            "courier": {
                "id": 8,
                "name": "ACS"
            },
            "status": "in_transit",
            "status_label": "Σε μεταφορά",
            "delivered_at": null,
            "returned_at": null,
            "cod_amount": "49.90",
            "cod_paid": false,
            "last_event": {
                "description": "Σε διαδικασία διανομής",
                "code": 13,
                "occurred_at": "2026-10-14T11:00:00Z"
            },
            "tracking_url": "https://courierhub.gr/t/mQ7CdcKy",
            "created_at": "2026-10-14T07:05:00Z",
            "order": {
                "id": 1,
                "order_id": "5012345678901",
                "order_number": "#12345",
                "store": {
                    "id": 1,
                    "name": "Το Κατάστημά μου"
                }
            }
        },
        "previous_status": "created",
        "new_events": [
            {
                "occurred_at": "2026-10-14T07:30:00Z",
                "description": "Παραλαβή από τον αποστολέα",
                "details": null,
                "code": 1
            },
            {
                "occurred_at": "2026-10-14T10:15:00Z",
                "description": "Αναχώρηση από το κέντρο διαλογής Αθήνας",
                "details": {
                    "station": "ΑΘΗ",
                    "depot": {},
                    "scans": [
                        "A1",
                        "B7"
                    ]
                },
                "code": 12
            },
            {
                "occurred_at": "2026-10-14T11:00:00Z",
                "description": "Σε διαδικασία διανομής",
                "details": "Ο οδηγός είναι καθ' οδόν",
                "code": 13
            }
        ]
    }
}

Μια δεύτερη αλλαγή, η παράδοση (in_transit -> delivered):

json
{
    "id": "933aa02a-d56f-4a28-b6e5-11f11e664419",
    "type": "shipment.updated",
    "created_at": "2026-10-15T06:00:00Z",
    "data": {
        "shipment": {
            "voucher_number": "7012345678",
            "courier": {
                "id": 8,
                "name": "ACS"
            },
            "status": "delivered",
            "status_label": "Παραδόθηκε",
            "delivered_at": "2026-10-15T05:40:00Z",
            "returned_at": null,
            "cod_amount": "49.90",
            "cod_paid": true,
            "last_event": {
                "description": "Παραδόθηκε",
                "code": 7,
                "occurred_at": "2026-10-15T05:40:00Z"
            },
            "tracking_url": "https://courierhub.gr/t/mQ7CdcKy",
            "created_at": "2026-10-14T07:05:00Z",
            "order": {
                "id": 1,
                "order_id": "5012345678901",
                "order_number": "#12345",
                "store": {
                    "id": 1,
                    "name": "Το Κατάστημά μου"
                }
            }
        },
        "previous_status": "in_transit",
        "new_events": [
            {
                "occurred_at": "2026-10-15T05:40:00Z",
                "description": "Παραδόθηκε",
                "details": {
                    "signed_by": "Μ. Παπαδοπούλου"
                },
                "code": 7
            }
        ]
    }
}

shipment.deleted ​

Το shipment είναι η αποστολή όπως ήταν τη στιγμή της διαγραφής. Το previous_status είναι η κατάσταση του τελευταίου webhook που δημιουργήθηκε για την αποστολή σε αυτό το endpoint (ακόμα κι αν εκκρεμεί ή απέτυχε, null αν δεν υπάρχει) και το new_events είναι πάντα κενό.

json
{
    "id": "c2bc5817-b040-4196-a78b-161b82cef8da",
    "type": "shipment.deleted",
    "created_at": "2026-10-16T05:00:00Z",
    "data": {
        "shipment": {
            "voucher_number": "7012345678",
            "courier": {
                "id": 8,
                "name": "ACS"
            },
            "status": "delivered",
            "status_label": "Παραδόθηκε",
            "delivered_at": "2026-10-15T05:40:00Z",
            "returned_at": null,
            "cod_amount": "49.90",
            "cod_paid": true,
            "last_event": {
                "description": "Παραδόθηκε",
                "code": 7,
                "occurred_at": "2026-10-15T05:40:00Z"
            },
            "tracking_url": "https://courierhub.gr/t/mQ7CdcKy",
            "created_at": "2026-10-14T07:05:00Z",
            "order": {
                "id": 1,
                "order_id": "5012345678901",
                "order_number": "#12345",
                "store": {
                    "id": 1,
                    "name": "Το Κατάστημά μου"
                }
            }
        },
        "previous_status": "delivered",
        "new_events": []
    }
}

ping ​

json
{
    "id": "013c706e-f8d6-4cd9-9923-749f660c333a",
    "type": "ping",
    "created_at": "2026-10-15T07:00:00Z",
    "data": {
        "endpoint_id": 1,
        "message": "Test event from CourierHub."
    }
}

Κεφαλίδες (headers) ​

HeaderΠεριγραφή
Content-TypeΠάντα application/json.
User-AgentCourierHub-Webhooks/1.0.
CourierHub-EventΟ τύπος του γεγονότος (shipment.created, ...).
CourierHub-DeliveryUUID του γεγονότος (όπως το id του body). Ίδιο σε όλες τις επαναλήψεις: χρησιμοποιήστε το για idempotency.
CourierHub-TimestampUnix timestamp (δευτερόλεπτα) της υπογραφής. Αλλάζει σε κάθε προσπάθεια.
CourierHub-SignatureΗ υπογραφή, v1= + hex. Δείτε Επαλήθευση υπογραφής.

Παράδειγμα κεφαλίδων του ping (POST στο URL σας):

text
Content-Type: application/json
User-Agent: CourierHub-Webhooks/1.0
CourierHub-Event: ping
CourierHub-Delivery: 013c706e-f8d6-4cd9-9923-749f660c333a
CourierHub-Timestamp: 1792047600
CourierHub-Signature: v1=a4c165a9896dc2dd31bace350c6fe00e4edd69cbdb3c882cb09a3bb8d1ebaf2d

Επαλήθευση υπογραφής ​

Επαληθεύετε πάντα την υπογραφή πριν εμπιστευτείτε μια αποστολή: το URL σας είναι δημόσιο και οποιοσδήποτε μπορεί να στείλει αιτήματα σε αυτό.

Η υπογραφή είναι HMAC-SHA256 με κλειδί το secret του endpoint (whsec_...) πάνω στο string "{timestamp}.{raw_body}", δηλαδή το timestamp της κεφαλίδας CourierHub-Timestamp, μια τελεία και το σώμα του αιτήματος. Το αποτέλεσμα γράφεται σε hex και στέλνεται ως v1=<hex>.

Βήματα:

  1. Διαβάστε το ακατέργαστο (raw) body του αιτήματος, ακριβώς όπως ήρθε.
  2. Αν το CourierHub-Timestamp διαφέρει από την τρέχουσα ώρα πάνω από 5 λεπτά (προς τα πίσω ή προς τα μπρος), απορρίψτε το αίτημα (προστασία από replay).
  3. Υπολογίστε v1= + hex(HMAC-SHA256(secret, timestamp + "." + raw_body)).
  4. Συγκρίνετε με την CourierHub-Signature με σύγκριση σταθερού χρόνου (hash_equals / crypto.timingSafeEqual), όχι με ==.

Χρησιμοποιήστε το RAW body

Η υπογραφή υπολογίζεται πάνω στα ακριβή bytes που στάλθηκαν. Αν διαβάσετε το JSON, το μετατρέψετε σε αντικείμενο και το ξανακωδικοποιήσετε (json_decode / JSON.parse και μετά json_encode / JSON.stringify), μπορεί να αλλάξει η μορφοποίηση, τα escapes χαρακτήρων ή τα κενά αντικείμενα {} (που σε PHP γίνονται []) και η υπογραφή δεν θα ταιριάζει. Στο Express χρησιμοποιήστε express.raw() και όχι express.json() στη διαδρομή του webhook.

PHP ​

php
<?php

/**
 * Επαληθεύει την υπογραφή ενός webhook του CourierHub.
 *
 * @param string $secret     το secret του endpoint (whsec_...)
 * @param string $rawBody    το ακατέργαστο body του αιτήματος
 * @param string $timestamp  η κεφαλίδα CourierHub-Timestamp
 * @param string $signature  η κεφαλίδα CourierHub-Signature
 * @param int    $tolerance  μέγιστη διαφορά ώρας σε δευτερόλεπτα (5 λεπτά)
 */
function verifyCourierHubSignature(string $secret, string $rawBody, string $timestamp, string $signature, int $tolerance = 300): bool
{
    if (! ctype_digit($timestamp) || abs(time() - (int) $timestamp) > $tolerance) {
        return false;
    }

    $expected = 'v1='.hash_hmac('sha256', $timestamp.'.'.$rawBody, $secret);

    return hash_equals($expected, $signature);
}

Χρήση σε endpoint:

php
<?php

$rawBody = file_get_contents('php://input');

$valid = verifyCourierHubSignature(
    getenv('COURIERHUB_WEBHOOK_SECRET'),
    $rawBody,
    $_SERVER['HTTP_COURIERHUB_TIMESTAMP'] ?? '',
    $_SERVER['HTTP_COURIERHUB_SIGNATURE'] ?? '',
);

if (! $valid) {
    http_response_code(401);
    exit;
}

// Απαντήστε γρήγορα και επεξεργαστείτε ασύγχρονα (π.χ. βάλτε το σε ουρά).
$event = json_decode($rawBody, true);
http_response_code(200);

Στο Laravel χρησιμοποιήστε $request->getContent() για το raw body και $request->header('CourierHub-Timestamp') για τις κεφαλίδες.

Node.js ​

js
const crypto = require('node:crypto');

/**
 * Επαληθεύει την υπογραφή ενός webhook του CourierHub.
 * rawBody: Buffer ή string με το ακατέργαστο body.
 */
function verifyCourierHubSignature(secret, rawBody, timestamp, signature, toleranceSeconds = 300) {
  if (!/^\d+$/.test(timestamp ?? '')) {
    return false;
  }

  if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > toleranceSeconds) {
    return false;
  }

  const expected =
    'v1=' +
    crypto.createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest('hex');

  const a = Buffer.from(expected);
  const b = Buffer.from(signature ?? '');

  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

module.exports = { verifyCourierHubSignature };

Χρήση με Express:

js
const express = require('express');
const { verifyCourierHubSignature } = require('./verify');

const app = express();

// express.raw: το req.body είναι Buffer με τα ακριβή bytes του αιτήματος.
app.post('/hooks/courierhub', express.raw({ type: 'application/json' }), (req, res) => {
  const valid = verifyCourierHubSignature(
    process.env.COURIERHUB_WEBHOOK_SECRET,
    req.body,
    req.get('CourierHub-Timestamp'),
    req.get('CourierHub-Signature'),
  );

  if (!valid) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  // Απαντήστε γρήγορα και επεξεργαστείτε ασύγχρονα.
  res.sendStatus(200);
});

app.listen(3000);

Δοκιμή της υπογραφής σας ​

Για να ελέγξετε την υλοποίησή σας: με secret whsec_8nQ2vK5tR1yLwZ7pD4mC9sJ6hB3fG0aE2uXcT1oVbNk και CourierHub-Timestamp: 1792047600, το παρακάτω σώμα (σε μία γραμμή, χωρίς τελικό newline) δίνει υπογραφή v1=a4c165a9896dc2dd31bace350c6fe00e4edd69cbdb3c882cb09a3bb8d1ebaf2d:

text
{"id":"013c706e-f8d6-4cd9-9923-749f660c333a","type":"ping","created_at":"2026-10-15T07:00:00Z","data":{"endpoint_id":1,"message":"Test event from CourierHub."}}

Απάντηση και retries ​

Η αποστολή θεωρείται επιτυχής μόνο όταν το σύστημά σας απαντήσει με HTTP status 2xx. Οτιδήποτε άλλο θεωρείται αποτυχία και η αποστολή επαναλαμβάνεται:

  • Μη-2xx status (συμπεριλαμβανομένων των redirects 3xx: δεν ακολουθούνται redirects).
  • Timeout: το CourierHub περιμένει έως 10 δευτερόλεπτα για απάντηση (και 5 για τη σύνδεση).
  • Σφάλμα σύνδεσης ή TLS, ή host που δεν επιλύεται (DNS).

Γίνονται έως 8 προσπάθειες συνολικά, με αυξανόμενη καθυστέρηση, σε διάστημα περίπου 22 ωρών:

ΠροσπάθειαΚαθυστέρηση από την προηγούμενηΠερίπου από την αρχή
1άμεσα0
21 λεπτό1 λεπτό
35 λεπτά6 λεπτά
415 λεπτά21 λεπτά
51 ώρα1 ώρα 21 λεπτά
63 ώρες4 ώρες 21 λεπτά
76 ώρες10 ώρες 21 λεπτά
812 ώρες22 ώρες 21 λεπτά

Μετά την 8η αποτυχία η αποστολή χαρακτηρίζεται failed και δεν ξαναδοκιμάζεται. Δείτε την κατάστασή της στο ιστορικό αποστολών ή στο περιβάλλον πελάτη (Ρυθμίσεις > Webhooks > επεξεργασία endpoint > "Αποστολές webhook").

Ειδικές περιπτώσεις:

  • Αν το URL δεν περνά πια τον έλεγχο ασφαλείας (π.χ. ο host δείχνει πλέον σε ιδιωτική διεύθυνση), η αποστολή αποτυγχάνει αμέσως χωρίς retries.
  • Αν το endpoint είναι ανενεργό τη στιγμή της προσπάθειας, η αποστολή αποτυγχάνει χωρίς να σταλεί.
  • Οι αποστολές διατηρούνται στο ιστορικό για 30 ημέρες.
  • Τα retries υπογράφονται με το τρέχον secret τη στιγμή της προσπάθειας. Μετά από rotate-secret χρησιμοποιείται το νέο, ακόμα και για αποστολές που είχαν δημιουργηθεί πριν.

Idempotency ​

Λόγω retries, το ίδιο γεγονός μπορεί να φτάσει περισσότερες από μία φορές (π.χ. αν το σύστημά σας επεξεργάστηκε το αίτημα αλλά η απάντηση χάθηκε). Σε όλες τις προσπάθειες η κεφαλίδα CourierHub-Delivery (και το id του body) έχει την ίδια τιμή. Αποθηκεύστε τα ids που έχετε επεξεργαστεί και αγνοήστε (απαντώντας 2xx) όσα έχετε ήδη δει.

Το CourierHub-Delivery είναι το κλειδί idempotency για τις επαναλήψεις του ίδιου webhook. Δεν ταυτίζει τα events: ένα event που ξαναστέλνεται μετά από αποτυχία έρχεται σε άλλο webhook με άλλο CourierHub-Delivery.

Το CourierHub-Timestamp είναι η ώρα αποστολής της συγκεκριμένης προσπάθειας και μέρος της υπογραφής. Αλλάζει σε κάθε retry, γι' αυτό χρησιμοποιείται για προστασία από replay (5 λεπτά) και όχι για idempotency ή σειρά.

Σειρά και διπλότυπα ​

  • Τα webhooks της ίδιας αποστολής μπορεί να φτάσουν εκτός σειράς (π.χ. retry παλαιότερου webhook προσγειώνεται μετά από νεότερο). Κρατήστε ανά αποστολή το created_at του φακέλου (envelope) του τελευταίου webhook που επεξεργαστήκατε και αγνοήστε για την κατάσταση της αποστολής όσα έχουν παλαιότερο created_at. Το data.shipment είναι πάντα ολόκληρη η κατάσταση εκείνη τη στιγμή. Μην ταξινομείτε με το CourierHub-Timestamp.
  • Τα new_events είναι παλαιότερο πρώτο. Ένα event που ξαναστάλθηκε μπορεί να εμφανιστεί ξανά: αποθηκεύστε τα events με μοναδικό κλειδί (occurred_at, code, description) και αγνοήστε τα διπλότυπα.
  • Το previous_status αναφέρεται στην κατάσταση πριν από την παλαιότερη αλλαγή του webhook, οπότε μετά από αποτυχία μπορεί να είναι παλαιότερο από την τελευταία κατάσταση που λάβατε. Όταν δεν υπάρχει τέτοια αλλαγή, είναι η κατάσταση του τελευταίου webhook που δημιουργήθηκε για την αποστολή σε αυτό το endpoint, ακόμα κι αν αυτό εκκρεμεί ή απέτυχε, άρα όχι απαραίτητα κάτι που λάβατε. Μην το χρησιμοποιείτε ως μοναδική πηγή αλήθειας: η τρέχουσα κατάσταση είναι το data.shipment.status.

Απαντήστε γρήγορα ​

Απαντήστε με 2xx αμέσως και κάντε την επεξεργασία ασύγχρονα (ουρά, background job). Αν η επεξεργασία κρατήσει πάνω από 10 δευτερόλεπτα, η αποστολή θα θεωρηθεί αποτυχημένη και θα επαναληφθεί, ενώ εσείς μπορεί να την έχετε ήδη επεξεργαστεί. Το σώμα της απάντησής σας δεν χρησιμοποιείται (αποθηκεύονται τα πρώτα 2048 bytes για διάγνωση).

Δοκιμή και ενεργοποίηση ​

Το γεγονός ping σας επιτρέπει να δοκιμάσετε ένα URL και την επαλήθευση υπογραφής πριν δεχτείτε πραγματικά δεδομένα. Στέλνεται με:

  • το API: POST /webhook-endpoints/{id}/test, ή
  • το κουμπί Αποστολή δοκιμής στο περιβάλλον πελάτη (Ρυθμίσεις > Webhooks > μενού της γραμμής του endpoint).

Το ping υπογράφεται όπως τα πραγματικά γεγονότα, γίνεται μία προσπάθεια (χωρίς retries), καταγράφεται στο ιστορικό αποστολών και λειτουργεί και σε ανενεργά endpoints.

Βήμα προς βήμα ​

  1. Δημιουργήστε το endpoint ως ανενεργό: από Ρυθμίσεις > Webhooks (Νέο webhook, με το "Ενεργό" απενεργοποιημένο) ή με POST /webhook-endpoints και "is_active": false. Το URL πρέπει να είναι https στη θύρα 443 και δημόσιο.
  2. Αποθηκεύστε το secret (whsec_...) που εμφανίζεται μία φορά (στο παράθυρο "Secret υπογραφής" ή στην απάντηση του API) στις ρυθμίσεις του συστήματός σας. Αν το χάσετε, δημιουργήστε νέο με "Νέο secret" / rotate-secret.
  3. Υλοποιήστε τον παραλήπτη: επαλήθευση υπογραφής, άμεση απάντηση 2xx, ασύγχρονη επεξεργασία, idempotency με CourierHub-Delivery.
  4. Στείλτε δοκιμή ("Αποστολή δοκιμής" ή POST /webhook-endpoints/{id}/test) και βεβαιωθείτε ότι το success είναι true και ότι ο παραλήπτης σας δέχτηκε το ping.
  5. Επαληθεύστε την υπογραφή του ping με τον κώδικά σας. Δοκιμάστε και ότι απορρίπτει αλλοιωμένο body.
  6. Ενεργοποιήστε το endpoint: ενεργοποιήστε το "Ενεργό" στην επεξεργασία του webhook ή στείλτε PATCH /webhook-endpoints/{id} με {"is_active": true}.

Από εκεί και πέρα λαμβάνετε τα πραγματικά γεγονότα. Παρακολουθήστε το ιστορικό αποστολών για αποτυχίες.

Ασφάλεια ​

  • Επαληθεύετε πάντα την υπογραφή και το timestamp.
  • Κρατήστε το secret μυστικό και δεν το βάζετε σε κώδικα του browser. Αν διαρρεύσει, κάντε rotate-secret.
  • Το CourierHub στέλνει μόνο σε https και θύρα 443, σε δημόσιες διευθύνσεις.

Released under the MIT License.