Subscriptions are a way to get notified about the changes happening to your entities.
🦉
Listening for changes with webhook subscription
Open Recipe
Creating a Subscription
{
"name": "My Subscription",
"url": "https://my.webhook.com",
"eventTypes": [
"shipmentUpdated"
]
}
Will create POST messages to https://my.webhook.com for every shipment update, with the following example body:
{
"type": "shipmentUpdated",
"timestamp": "2023-09-14T07:03:30.714Z",
"shipmentId": "S111222",
"customerReference": "AAA123"
}
{
"type": "shipmentUpdated",
"timestamp": "2023-09-14T07:03:30.714Z",
"shipmentId": "S111222",
"customerReference": "AAA123"
}
This events typically contains:
- The type of update (e.g. shipmentUpdated)
- The timestamp of the update
- The Forto shipment ID, which can be seen in the SHIP application (E.g. https://ship.forto.com/shipment/S111222/overview)
- The customer reference sent by the customer for that shipment or booking request.
- Additional details specific to events
Validating webhook requests
Every subscription has a signatureKey
attribute which will be used to sign the requests we have. Webhook requests are sent with a x-hub-signature
header. The signature can be calculated on the receiving end and be compared to the value of the header to make sure the request is sent from us.
Calculating the HMAC
Signature is calculated with creating the SHA1 hash of the raw JSON body as a string. Below, you can find example code snippets in several languages.
import * as crypto from 'crypto'
function generateSignature(signatureKey, payload) {
return crypto.createHmac('sha1', signatureKey).update(Buffer.from(payload, 'utf-8')).digest('hex')
}
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import javax.xml.bind.DatatypeConverter;
public class HmacCalculator {
public static String generateSignature(String signatureKey, String payload) throws NoSuchAlgorithmException, UnsupportedEncodingException {
Mac sha1Hmac = Mac.getInstance("HmacSHA1");
SecretKeySpec secretKey = new SecretKeySpec(signatureKey.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
sha1Hmac.init(secretKey);
byte[] signatureBytes = sha1Hmac.doFinal(payload.getBytes(StandardCharsets.UTF_8));
return DatatypeConverter.printHexBinary(signatureBytes).toLowerCase()
}
}
import hmac
import hashlib
def create_hmac_sha1(signature_key, payload):
signature = hmac.new(signature_key.encode('utf-8'), payload.encode('utf-8'), hashlib.sha1)
return signature.hexdigest()
Event schemas
Below are the schemas for all the events being sent.
Shipment Created Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Shipment create event type | "shipmentCreated" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | ID of the related shipment | - | - | - |
customerReference | string | Customer Reference of the related shipment | - | - | - |
{
"type": "shipmentCreated",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"customerReference": "AAA123"
}
Shipment Updated Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Shipment updated event type | "shipmentUpdated" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | ID of the related shipment | - | - | - |
customerReference | string | Customer Reference of the related shipment | - | - | - |
{
"type": "shipmentUpdated",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"customerReference": "AAA123"
}
Shipment Deleted Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Shipment deleted event type | "shipmentDeleted" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | ID of the related shipment | - | - | - |
customerReference | string | Customer Reference of the related shipment | - | - | - |
{
"type": "shipmentDeleted",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"customerReference": "AAA123"
}
Transport Plan Updated Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Transport Plan Updated Event Type | "transportPlanUpdated" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | ID of the related shipment | - | - | - |
milestone | string | Milestone that triggered the event | "cargoReady" , "pickup" , "departure" , "transshipmentArrival" , "transshipmentDeparture" , "transshipmentArrival2" , "transshipmentDeparture2" , "transshipmentArrival3" , "transshipmentDeparture3" , "transshipmentArrival4" , "transshipmentDeparture4" , "arrival" , "delivery" , "containerGateOut" , "containerGateIn" , "containerEmptyReturn" , "containerEmptyPickup" , "customsStopOrigin" , "customsStopDestination" , "containerDischargedTransshipment1" , "containerDischargedTransshipment2" , "containerDischargedTransshipment3" , "containerDischargedTransshipment4" , "containerLoadedTransshipment1" , "containerLoadedTransshipment2" , "containerLoadedTransshipment3" , "containerLoadedTransshipment4" , "transitAirportArrival" , "transitAirportArrival2" , "transitAirportArrival3" , "transitAirportArrival4" , "transitAirportDeparture" , "transitAirportDeparture2" , "transitAirportDeparture3" , "transitAirportDeparture4" , "containerGateInMultimodalTerminalOrigin" , "containerGateOutMultimodalTerminalOrigin" , "containerGateInMultimodalTerminalDestination" , "containerGateOutMultimodalTerminalDestination" , "containerDischargedDestination" , "containerDepartureAtShipper" , "verifiedGrossMarginStop" , "departureFromPointOfReceipt" , "containerLoadingOrigin" , "containerEmptyDeparture" , "containerDepartureDelivery" , "arrivalPartner" , "departurePartner" , "arrivalOrigin" , "receivedFromFlight" | - | required |
isActual | boolean | Is this milestone actually happened | - | - | required |
isConfirmed | boolean | Is this milestone confirmed | - | - | required |
trackableId | string | Trackable ID of the container | - | - | required |
containerNumber | string | Container number | - | - | - |
shipmentCustomerReference | string | Customer Reference of the Shipment | - | - | - |
containerCustomerReference | string | Customer Reference of the Container | - | - | - |
{
"type": "transportPlanUpdated",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"milestone": "cargoReady",
"isActual": true,
"isConfirmed": true,
"trackableId": "64b786881185bb42abcdefgh",
"containerNumber": "TEMU2341234",
"shipmentCustomerReference": "AAA123",
"containerCustomerReference": "CO123"
}
Customs Status Updated Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Customs Status Updated Event Type | "customsStatusUpdated" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | ID of the related shipment | - | - | - |
customerReference | string | Customer Reference of the related shipment | - | - | - |
containers | array | Tracking Events. | - | - | required |
containers.id | string | ID of the container | - | - | required |
containers.customerReference | string | Reference for the container | - | - | required |
containers.containerNumber | string | Container number | - | - | required |
containers.status | string | Latest status given for the container | - | - | required |
containers.description | string | Human readable description for the customs status | - | - | - |
containers.newStatus | boolean | Defined if this is a newly acquired information | - | - | - |
containers.customsReferenceNumber | array<array> | Reference numbers of the container for customs | - | - | - |
containers.customsReferenceNumber (single item) | array | - | - | - | - |
{
"type": "customsStatusUpdated",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"customerReference": "string",
"containers": [
{
"id": "649d8d9306d6c4a111222a12",
"customerReference": "AAA123",
"containerNumber": "TEMU2341234",
"status": "declaration-filed",
"description": "Declaration filed",
"newStatus": true,
"customsReferenceNumber": [
"AAA123",
"BBB456"
]
}
]
}
Document Updated Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Document Updated Event Type | "documentUpdated" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | ID of the related shipment | - | - | - |
action | string | What happened to the document | "added" , "updated" , "deleted" | - | required |
documentId | string | Related Document ID | - | - | required |
documentName | string | Related Document Name | - | - | required |
documentCreatedAt | string | Creation date of the document | - | date-time | required |
documentType | string | Document type | - | - | required |
shipmentCustomerReference | string | Customer Reference of the Shipment | - | - | - |
{
"type": "documentUpdated",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"action": "added",
"documentId": "65030a3b2777d6c308181aac",
"documentName": "document_name.pdf",
"documentCreatedAt": "2019-08-24T14:15:22Z",
"documentType": "shipmentInvoice",
"shipmentCustomerReference": "AAA123"
}
Shipment Invoice Created Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Shipment Invoice Created Event Type | "shipmentInvoiceCreated" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | Shipment ID | - | - | - |
invoiceNumber | string | - | - | - | required |
invoiceType | string | - | "creditNote" , "freight" , "customs" , "additionalCharges" | - | required |
status | string | - | "Cancelled" , "Outstanding" , "Overdue" , "Paid" , "Cleared" | - | required |
lineItems | array | - | - | - | required |
lineItems.description | string | - | - | - | required |
lineItems.costCode | string | - | - | - | - |
lineItems.quantity | number | - | - | >= 0 | required |
lineItems.vatClass | string | - | - | - | required |
lineItems.exchangeRate | number | Only exists if currencies are different | - | >= 0 | - |
lineItems.unitPrice | object | - | - | - | required |
lineItems.unitPrice.amount | number | - | - | - | required |
lineItems.unitPrice.currency | string | - | "AED" , "AUD" , "BDT" , "BGN" , "BHD" , "BND" , "BRL" , "CAD" , "CHF" , "CNY" , "CYP" , "CZK" , "DKK" , "EEK" , "EUR" , "FJD" , "GBP" , "HKD" , "HRK" , "HUF" , "IDR" , "ILS" , "INR" , "IRR" , "ISK" , "JOD" , "JPY" , "KRW" , "KWD" , "LTL" , "LVL" , "MAD" , "MMK" , "MTL" , "MXN" , "MYR" , "NAD" , "NOK" , "NZD" , "OMR" , "PGK" , "PHP" , "PKR" , "PLN" , "QAR" , "ROL" , "RON" , "RUB" , "SAR" , "SEK" , "SGD" , "SIT" , "SKK" , "THB" , "TRL" , "TRY" , "TWD" , "USD" , "VND" , "ZAR" , "ZA" | - | required |
lineItems.netAmount | object | - | - | - | required |
lineItems.netAmount.amount | number | - | - | - | required |
lineItems.netAmount.currency | string | - | "AED" , "AUD" , "BDT" , "BGN" , "BHD" , "BND" , "BRL" , "CAD" , "CHF" , "CNY" , "CYP" , "CZK" , "DKK" , "EEK" , "EUR" , "FJD" , "GBP" , "HKD" , "HRK" , "HUF" , "IDR" , "ILS" , "INR" , "IRR" , "ISK" , "JOD" , "JPY" , "KRW" , "KWD" , "LTL" , "LVL" , "MAD" , "MMK" , "MTL" , "MXN" , "MYR" , "NAD" , "NOK" , "NZD" , "OMR" , "PGK" , "PHP" , "PKR" , "PLN" , "QAR" , "ROL" , "RON" , "RUB" , "SAR" , "SEK" , "SGD" , "SIT" , "SKK" , "THB" , "TRL" , "TRY" , "TWD" , "USD" , "VND" , "ZAR" , "ZA" | - | required |
issueDate | string | - | - | date-time | required |
netAmount | object | - | - | - | required |
netAmount.amount | number | - | - | - | required |
netAmount.currency | string | - | "AED" , "AUD" , "BDT" , "BGN" , "BHD" , "BND" , "BRL" , "CAD" , "CHF" , "CNY" , "CYP" , "CZK" , "DKK" , "EEK" , "EUR" , "FJD" , "GBP" , "HKD" , "HRK" , "HUF" , "IDR" , "ILS" , "INR" , "IRR" , "ISK" , "JOD" , "JPY" , "KRW" , "KWD" , "LTL" , "LVL" , "MAD" , "MMK" , "MTL" , "MXN" , "MYR" , "NAD" , "NOK" , "NZD" , "OMR" , "PGK" , "PHP" , "PKR" , "PLN" , "QAR" , "ROL" , "RON" , "RUB" , "SAR" , "SEK" , "SGD" , "SIT" , "SKK" , "THB" , "TRL" , "TRY" , "TWD" , "USD" , "VND" , "ZAR" , "ZA" | - | required |
grossAmount | object | - | - | - | required |
grossAmount.amount | number | - | - | - | required |
grossAmount.currency | string | - | "AED" , "AUD" , "BDT" , "BGN" , "BHD" , "BND" , "BRL" , "CAD" , "CHF" , "CNY" , "CYP" , "CZK" , "DKK" , "EEK" , "EUR" , "FJD" , "GBP" , "HKD" , "HRK" , "HUF" , "IDR" , "ILS" , "INR" , "IRR" , "ISK" , "JOD" , "JPY" , "KRW" , "KWD" , "LTL" , "LVL" , "MAD" , "MMK" , "MTL" , "MXN" , "MYR" , "NAD" , "NOK" , "NZD" , "OMR" , "PGK" , "PHP" , "PKR" , "PLN" , "QAR" , "ROL" , "RON" , "RUB" , "SAR" , "SEK" , "SGD" , "SIT" , "SKK" , "THB" , "TRL" , "TRY" , "TWD" , "USD" , "VND" , "ZAR" , "ZA" | - | required |
vatAmount | object | - | - | - | required |
vatAmount.amount | number | - | - | - | required |
vatAmount.currency | string | - | "AED" , "AUD" , "BDT" , "BGN" , "BHD" , "BND" , "BRL" , "CAD" , "CHF" , "CNY" , "CYP" , "CZK" , "DKK" , "EEK" , "EUR" , "FJD" , "GBP" , "HKD" , "HRK" , "HUF" , "IDR" , "ILS" , "INR" , "IRR" , "ISK" , "JOD" , "JPY" , "KRW" , "KWD" , "LTL" , "LVL" , "MAD" , "MMK" , "MTL" , "MXN" , "MYR" , "NAD" , "NOK" , "NZD" , "OMR" , "PGK" , "PHP" , "PKR" , "PLN" , "QAR" , "ROL" , "RON" , "RUB" , "SAR" , "SEK" , "SGD" , "SIT" , "SKK" , "THB" , "TRL" , "TRY" , "TWD" , "USD" , "VND" , "ZAR" , "ZA" | - | required |
link | string | Download link for the document | - | - | required |
linkValidUntil | string | Time that this download link is valid for downloading. Requesting again will generate another download link. | - | date-time | required |
dueDate | string | Only defined if invoice is not paid | - | date-time | - |
dueAmount | allOf | Only defined if invoice is not paid | - | - | - |
dueAmount.0 (allOf item) | object | - | - | - | - |
dueAmount.0.amount | number | - | - | - | required |
dueAmount.0.currency | string | - | "AED" , "AUD" , "BDT" , "BGN" , "BHD" , "BND" , "BRL" , "CAD" , "CHF" , "CNY" , "CYP" , "CZK" , "DKK" , "EEK" , "EUR" , "FJD" , "GBP" , "HKD" , "HRK" , "HUF" , "IDR" , "ILS" , "INR" , "IRR" , "ISK" , "JOD" , "JPY" , "KRW" , "KWD" , "LTL" , "LVL" , "MAD" , "MMK" , "MTL" , "MXN" , "MYR" , "NAD" , "NOK" , "NZD" , "OMR" , "PGK" , "PHP" , "PKR" , "PLN" , "QAR" , "ROL" , "RON" , "RUB" , "SAR" , "SEK" , "SGD" , "SIT" , "SKK" , "THB" , "TRL" , "TRY" , "TWD" , "USD" , "VND" , "ZAR" , "ZA" | - | required |
{
"type": "shipmentInvoiceCreated",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"invoiceNumber": "string",
"invoiceType": "creditNote",
"status": "Cancelled",
"lineItems": [
{
"description": "string",
"costCode": "string",
"quantity": 0,
"vatClass": "string",
"exchangeRate": 0,
"unitPrice": {
"amount": 0,
"currency": "AED"
},
"netAmount": {
"amount": 0,
"currency": "AED"
}
}
],
"issueDate": "2019-08-24T14:15:22Z",
"netAmount": {
"amount": 0,
"currency": "AED"
},
"grossAmount": {
"amount": 0,
"currency": "AED"
},
"vatAmount": {
"amount": 0,
"currency": "AED"
},
"link": "string",
"linkValidUntil": "2019-08-24T14:15:22Z",
"dueDate": "2019-08-24T14:15:22Z",
"dueAmount": {
"amount": 0,
"currency": "AED"
}
}
Scheduled Report Generated Event
Name | Type | Description | Value | Constraints | Notes |
---|---|---|---|---|---|
type | string | Scheduled Report Generated Event Type | "scheduledReportGenerated" | - | required |
timestamp | string | Timestamp of the event | - | date-time | required |
shipmentId | string | ID of the related shipment | - | - | - |
reports | array | The reports that are successfully generated. | - | - | required |
reports.id | string | ID of the report | - | - | required |
reports.documentId | string | ID of the document | - | - | required |
reports.documentName | string | Name of the report generated | - | - | required |
reports.documentCreatedAt | string | Name of the report generated | - | - | required |
reports.format | string | Format of the report | "csv" , "xlsx" , "pdf" | - | required |
reports.reportTemplateId | string | ID of the report template | - | - | required |
{
"type": "scheduledReportGenerated",
"timestamp": "2019-08-24T14:15:22Z",
"shipmentId": "S111222",
"reports": [
{
"id": "6502a1d58cac3c77dc97b112",
"documentId": "6502a1d58cac3c77dc97b112.csv",
"documentName": "scheduled_report_1.csv",
"documentCreatedAt": "string",
"format": "csv",
"reportTemplateId": "R37065"
}
]
}