Webhooks API — Real-Time Tracking Push Notifications | WhereParcel
Subscribe to real-time shipment tracking updates via webhooks. Push notifications on delivery status changes, automatic exponential-backoff retries, signed payloads, event filtering.
Register webhook (billable)
Overview
Register a webhook for parcel tracking. Supports 2 modes:
recurring: true— Continuous monitoring until delivery (subscription). Recommended for most integrations. Register once and receive push notifications whenever the tracking status changes. Ideal for keeping your database in sync with delivery progress.recurring: false— Query once only (one-time, default). Useful for one-off lookups where you don't need ongoing updates.
Why use webhooks? Polling /v2/track repeatedly wastes your request quota. A single parcel typically has 5–10 status changes over its lifecycle. With webhooks, you receive only those 5–10 updates instead of making hundreds of polling requests.
Billable API, supports up to 100 items per registration.
What we POST to your endpoint. Content-Type is application/json; we expect a 2xx response within 10 seconds. Redirects are not followed.
{
"event": "tracking.updated",
"timestamp": "2026-08-21T04:15:22.184Z",
"data": {
"requestId": "req_abc123",
"timestamp": "2026-08-21T04:15:22.100Z",
"trackingItems": [
{
"carrier": { "code": "us.usps", "name": "USPS", "country": "US" },
"trackingNumber": "9400111206206406260787",
"status": "success",
"currentStatus": "in_transit",
"hasChange": true,
"trackingData": { "deliveryStatus": "in_transit", "events": [] }
}
],
"isPeriodicUpdate": true,
"hasChanges": true,
"changedItemCount": 1,
"queriedItemCount": 1,
"deliveredItemCount": 0,
"changeId": "chg_xyz789"
}
}
Fields:
event — tracking.registered on the first lookup after you register, tracking.updated on every later poll.
data.requestId — the subscription id returned by this endpoint.
data.trackingItems[] — one entry per tracked parcel. carrier is an object (code, name, country), not a string. status is success or error. currentStatus is the standardized delivery status. trackingData holds the full result. hasChange marks items whose status changed in this cycle.
data.isPeriodicUpdate, hasChanges, changedItemCount, queriedItemCount, deliveredItemCount, changeId — present only on recurring updates, not on the first tracking.registered delivery.
Headers: X-WhereParcel-Event, X-WhereParcel-Timestamp, and X-WhereParcel-Signature — an HMAC-SHA256 hex digest of the exact raw JSON body, keyed with your endpoint secret. Verify it by recomputing the digest over the raw body before parsing.
If you register without webhookEndpointId, nothing is pushed to you. That is the pull mode: retrieve results with GET /v2/webhooks/subscriptions/{requestId} or POST /v2/webhooks/results. To receive the payload above, create an endpoint with POST /v2/webhook-endpoints and pass its id as webhookEndpointId.
Example Request
// npm install whereparcel
import { WhereParcel } from 'whereparcel';
const wp = new WhereParcel('wp_test_public_demo_d5waw8abfqor', 'sk_test_public_demo_mj7ya1taqmaqfkv6lpwe');
const result = await wp.registerWebhook({
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "123456789012",
"clientId": "order-001"
},
{
"carrier": "us.fedex",
"trackingNumber": "612938472651",
"postalCode": "10001",
"clientId": "order-002"
}
],
"recurring": true,
"webhookEndpointId": "endpoint-id-123"
});
console.log(result);curl -X POST https://api.whereparcel.com/v2/webhooks/register \
-H "Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe" \
-H "Content-Type: application/json" \
-d '{
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "123456789012",
"clientId": "order-001"
},
{
"carrier": "us.fedex",
"trackingNumber": "612938472651",
"postalCode": "10001",
"clientId": "order-002"
}
],
"recurring": true,
"webhookEndpointId": "endpoint-id-123"
}'const response = await fetch('https://api.whereparcel.com/v2/webhooks/register', {
method: 'POST',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
},
body: JSON.stringify({
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "123456789012",
"clientId": "order-001"
},
{
"carrier": "us.fedex",
"trackingNumber": "612938472651",
"postalCode": "10001",
"clientId": "order-002"
}
],
"recurring": true,
"webhookEndpointId": "endpoint-id-123"
})
});
const data = await response.json();
console.log(data);<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.whereparcel.com/v2/webhooks/register');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
$headers = [
'Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = [
'trackingItems' => [
'0' => [
'carrier' => "us.fedex",
'trackingNumber' => "123456789012",
'clientId' => "order-001"
],
'1' => [
'carrier' => "us.fedex",
'trackingNumber' => "612938472651",
'postalCode' => "10001",
'clientId' => "order-002"
]
],
'recurring' => true,
'webhookEndpointId' => "endpoint-id-123"
];
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);import requests
import json
url = 'https://api.whereparcel.com/v2/webhooks/register'
headers = {
'Authorization': 'Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type': 'application/json'
}
data = {
'trackingItems': {
'0': {
'carrier': "us.fedex",
'trackingNumber': "123456789012",
'clientId': "order-001"
},
'1': {
'carrier': "us.fedex",
'trackingNumber': "612938472651",
'postalCode': "10001",
'clientId': "order-002"
}
},
'recurring': True,
'webhookEndpointId': "endpoint-id-123"
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.whereparcel.com/v2/webhooks/register"
payload := []byte(`{"trackingItems":[{"carrier":"us.fedex","trackingNumber":"123456789012","clientId":"order-001"},{"carrier":"us.fedex","trackingNumber":"612938472651","postalCode":"10001","clientId":"order-002"}],"recurring":true,"webhookEndpointId":"endpoint-id-123"}`)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
req.Header.Add("Authorization", "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe")
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))
}TIP: Replace the test API key with your actual API key from the dashboard.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
trackingItems | array | Required | Tracking items (up to 100) |
trackingItems[].carrier | string | Required | |
trackingItems[].trackingNumber | string | Required | |
trackingItems[].clientId | string | Optional | |
trackingItems[].postalCode | string | Optional | |
trackingItems[].phoneNumber | string | Optional | |
recurring | boolean | Optional | Tracking mode (optional, default: false)
- false: Query once only (default, safe) → webhookEndpointId optional
- true: Continuous monitoring (explicit request required) → webhookEndpointId required |
webhookEndpointId | string | Optional | Pre-registered Webhook Endpoint ID
- Required when recurring: true (validated by middleware)
- Optional when recurring: false
- If provided, sends POST on completion; if omitted, no notification sent
- Must register a Webhook Endpoint first |
Response
Success Response (200)
Webhook registered successfully
Response Body
{
"mode": "recurring",
"requestId": "req_abc123xyz",
"trackingItemCount": 2,
"webhookEndpointId": "endpoint-id-123",
"createdAt": "2026-02-05T10:00:00.000Z"
} Error Response (401)
Authentication failed
Response Body
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
} Error Response (429)
Rate limit exceeded
Response Body
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please try again later."
}
} List webhook subscriptions
Overview
Retrieve all registered webhook subscriptions (recurring + one-time). Free API - does not count towards usage quota.
Example Request
// npm install whereparcel
import { WhereParcel } from 'whereparcel';
const wp = new WhereParcel('wp_test_public_demo_d5waw8abfqor', 'sk_test_public_demo_mj7ya1taqmaqfkv6lpwe');
const subscriptions = await wp.getSubscriptions();
console.log(subscriptions);curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions \
-H "Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe" \
-H "Content-Type: application/json"const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions', {
method: 'GET',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.whereparcel.com/v2/webhooks/subscriptions');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
$headers = [
'Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);import requests
import json
url = 'https://api.whereparcel.com/v2/webhooks/subscriptions'
headers = {
'Authorization': 'Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type': 'application/json'
}
response = requests.get(url, headers=headers)
result = response.json()
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.whereparcel.com/v2/webhooks/subscriptions"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe")
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))
}TIP: Replace the test API key with your actual API key from the dashboard.
Response
Success Response (200)
Subscription list retrieved successfully
Response Body
[
{
"requestId": "req_abc123xyz",
"trackingItemCount": 2,
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "300718039335",
"clientId": "order-001",
"latestStatus": "delivered"
},
{
"carrier": "us.usps",
"trackingNumber": "9400111899223197428491",
"latestStatus": "in_transit"
}
],
"recurring": true,
"webhookEndpointId": "endpoint_xyz789",
"isActive": true,
"createdAt": "2026-02-05T10:00:00.000Z",
"updatedAt": "2026-02-05T14:30:00.000Z"
},
{
"requestId": "req_def456uvw",
"trackingItemCount": 1,
"trackingItems": [
{
"carrier": "us.usps",
"trackingNumber": "9400111899223197428492",
"latestStatus": "out_for_delivery"
}
],
"recurring": false,
"isActive": false,
"progress": {
"total": 1,
"completed": 1,
"succeeded": 1,
"failed": 0,
"percentage": 100
},
"createdAt": "2026-02-04T09:00:00.000Z",
"updatedAt": "2026-02-04T09:05:00.000Z",
"completedAt": "2026-02-04T09:05:00.000Z"
}
] Error Response (401)
Authentication failed
Response Body
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
} Get webhook subscription
Overview
Retrieve webhook subscription information for a specific requestId. Free API - does not count towards usage quota.
Example Request
// npm install whereparcel
const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}', {
method: 'GET',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId} \
-H "Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe" \
-H "Content-Type: application/json"const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}', {
method: 'GET',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
$headers = [
'Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);import requests
import json
url = 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}'
headers = {
'Authorization': 'Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type': 'application/json'
}
response = requests.get(url, headers=headers)
result = response.json()
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe")
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))
}TIP: Replace the test API key with your actual API key from the dashboard.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
requestId | string | Required | <p>requestId received during webhook registration</p>
Example: req_abc123xyz |
Response
Success Response (200)
Subscription info retrieved successfully
Response Body
{
"success": true,
"data": {
"requestId": "req_abc123xyz",
"userId": "user_abc123",
"apiKeyId": "key_def456",
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "123456789012",
"clientId": "order-001",
"latestStatus": "in_transit",
"usageCounted": true,
"status": "success",
"trackingData": {
"deliveryStatus": "in_transit",
"events": [
{
"timestamp": "2026-02-05T09:15:00-05:00",
"status": "in_transit",
"location": "Newark, NJ",
"description": "At local FedEx facility"
}
],
"lastUpdated": "2026-02-05T10:00:00-05:00"
}
}
],
"trackingIndex": [
"us.fedex:123456789012"
],
"clientIdIndex": [
"order-001"
],
"recurring": true,
"webhookEndpointId": "endpoint-id-123",
"isActive": true,
"createdAt": "2026-02-05T10:00:00.000Z",
"updatedAt": "2026-02-05T10:00:00.000Z"
}
} Error Response (401)
Authentication failed
Response Body
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
} Error Response (404)
requestId not found
Response Body
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "The requested resource was not found"
}
} Delete webhook subscription
Overview
Cancel a webhook subscription and stop monitoring. For recurring webhooks, monitoring stops immediately. Free API - does not count towards usage quota.
Example Request
// npm install whereparcel
const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}', {
method: 'DELETE',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);curl -X DELETE https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId} \
-H "Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe" \
-H "Content-Type: application/json"const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}', {
method: 'DELETE',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
$headers = [
'Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);import requests
import json
url = 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}'
headers = {
'Authorization': 'Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type': 'application/json'
}
response = requests.delete(url, headers=headers)
result = response.json()
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("Authorization", "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe")
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))
}TIP: Replace the test API key with your actual API key from the dashboard.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
requestId | string | Required | <p>requestId of the webhook to delete</p>
Example: req_abc123xyz |
Response
Success Response (200)
Subscription deleted successfully
Response Body
{
"deleted": true,
"requestId": "req_abc123xyz",
"message": "Webhook subscription deleted successfully"
} Error Response (401)
Authentication failed
Response Body
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
} Error Response (404)
requestId not found
Response Body
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "The requested resource was not found"
}
} List webhook change history
Overview
Retrieve all change history (delivery status changes) for a specific webhook subscription. Free API - does not count towards usage quota.
Example Request
// npm install whereparcel
const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes', {
method: 'GET',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes \
-H "Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe" \
-H "Content-Type: application/json"const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes', {
method: 'GET',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
$headers = [
'Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);import requests
import json
url = 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes'
headers = {
'Authorization': 'Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type': 'application/json'
}
response = requests.get(url, headers=headers)
result = response.json()
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe")
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))
}TIP: Replace the test API key with your actual API key from the dashboard.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
requestId | string | Required | <p>requestId received during webhook registration</p>
Example: req_abc123xyz |
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | Optional | <p>Maximum number of records to return (default 20, max 100). Newest first.</p>
|
Response
Success Response (200)
Change history retrieved successfully
Response Body
{
"success": true,
"data": {
"requestId": "req_abc123xyz",
"changes": [
{
"changeId": "change_1770282900000",
"timestamp": "2026-02-05T09:15:00.000Z",
"changedItemCount": 1,
"preview": {
"trackingNumbers": [
"1Z999AA10123456784"
]
}
},
{
"changeId": "change_1770301800000",
"timestamp": "2026-02-05T14:30:00.000Z",
"changedItemCount": 2,
"preview": {
"trackingNumbers": [
"1Z999AA10123456784",
"1Z999AA10123456785"
]
}
}
],
"pagination": {
"limit": 20,
"hasMore": false
}
}
} Error Response (401)
Authentication failed
Response Body
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
} Error Response (404)
requestId not found
Response Body
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "The requested resource was not found"
}
} Get single change event
Overview
Retrieve detailed information for a specific change event. Free API - does not count towards usage quota.
Example Request
// npm install whereparcel
const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes/{changeId}', {
method: 'GET',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes/{changeId} \
-H "Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe" \
-H "Content-Type: application/json"const response = await fetch('https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes/{changeId}', {
method: 'GET',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
}
});
const data = await response.json();
console.log(data);<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes/{changeId}');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
$headers = [
'Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);import requests
import json
url = 'https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes/{changeId}'
headers = {
'Authorization': 'Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type': 'application/json'
}
response = requests.get(url, headers=headers)
result = response.json()
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes/{changeId}"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe")
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))
}TIP: Replace the test API key with your actual API key from the dashboard.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
requestId | string | Required | <p>requestId received during webhook registration</p>
Example: req_abc123xyz |
changeId | string | Required | <p>Change event ID</p>
Example: change_def456 |
Response
Success Response (200)
Change event retrieved successfully
Response Body
{
"success": true,
"data": {
"changeId": "change_1770282900000",
"requestId": "req_abc123xyz",
"timestamp": "2026-02-05T09:15:00.000Z",
"changedItemCount": 1,
"changedItems": [
{
"carrier": "us.ups",
"trackingNumber": "1Z999AA10123456784",
"previousStatus": "in_transit",
"currentStatus": "out_for_delivery",
"trackingData": {
"deliveryStatus": "out_for_delivery",
"lastUpdated": "2026-02-05T09:15:00.000Z",
"events": [
{
"timestamp": "2026-02-05T08:00:00+09:00",
"status": "in_transit",
"location": "Newark, NJ",
"description": "In transit"
},
{
"timestamp": "2026-02-05T09:15:00+09:00",
"status": "out_for_delivery",
"location": "New York, NY",
"description": "Out for delivery"
}
]
}
}
]
}
} Error Response (401)
Authentication failed
Response Body
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing API key"
}
} Error Response (404)
requestId or changeId not found
Response Body
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "The requested resource was not found"
}
} Batch search webhook results
Overview
Search webhook results for multiple parcels at once. Use this to retrieve final results of registered webhooks.
Search methods:
- Search by carrier + trackingNumber
- Search by clientId
- Both methods can be used simultaneously
How it works:
- Searches using trackingIndex/clientIdIndex fields in the tracking_requests collection
- Returns only the latest document when multiple documents exist for the same tracking number
- Batch processed due to Firestore array-contains-any limit (10) (up to 10 queries for 100 items)
Free API - does not count towards usage quota.
Example Request
// npm install whereparcel
const response = await fetch('https://api.whereparcel.com/v2/webhooks/results', {
method: 'POST',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
},
body: JSON.stringify({
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "300718039335"
},
{
"carrier": "us.fedex",
"trackingNumber": "612938472651",
"postalCode": "10001"
}
]
})
});
const data = await response.json();
console.log(data);curl -X POST https://api.whereparcel.com/v2/webhooks/results \
-H "Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe" \
-H "Content-Type: application/json" \
-d '{
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "300718039335"
},
{
"carrier": "us.fedex",
"trackingNumber": "612938472651",
"postalCode": "10001"
}
]
}'const response = await fetch('https://api.whereparcel.com/v2/webhooks/results', {
method: 'POST',
headers: {
"Authorization": "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe",
"Content-Type": "application/json"
},
body: JSON.stringify({
"trackingItems": [
{
"carrier": "us.fedex",
"trackingNumber": "300718039335"
},
{
"carrier": "us.fedex",
"trackingNumber": "612938472651",
"postalCode": "10001"
}
]
})
});
const data = await response.json();
console.log(data);<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.whereparcel.com/v2/webhooks/results');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
$headers = [
'Authorization: Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = [
'trackingItems' => [
'0' => [
'carrier' => "us.fedex",
'trackingNumber' => "300718039335"
],
'1' => [
'carrier' => "us.fedex",
'trackingNumber' => "612938472651",
'postalCode' => "10001"
]
]
];
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
print_r($result);import requests
import json
url = 'https://api.whereparcel.com/v2/webhooks/results'
headers = {
'Authorization': 'Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe',
'Content-Type': 'application/json'
}
data = {
'trackingItems': {
'0': {
'carrier': "us.fedex",
'trackingNumber': "300718039335"
},
'1': {
'carrier': "us.fedex",
'trackingNumber': "612938472651",
'postalCode': "10001"
}
}
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
print(result)package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
url := "https://api.whereparcel.com/v2/webhooks/results"
payload := []byte(`{"trackingItems":[{"carrier":"us.fedex","trackingNumber":"300718039335"},{"carrier":"us.fedex","trackingNumber":"612938472651","postalCode":"10001"}]}`)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
req.Header.Add("Authorization", "Bearer wp_test_public_demo_d5waw8abfqor:sk_test_public_demo_mj7ya1taqmaqfkv6lpwe")
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))
}TIP: Replace the test API key with your actual API key from the dashboard.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
trackingItems | array | Required | |
Response
Success Response (200)
Batch search completed successfully
Response Body
{
"results": {
"us.fedex:300718039335": {
"source": "bulk",
"data": {
"deliveryStatus": "delivered",
"events": [
{
"timestamp": "2026-02-05T14:30:00-05:00",
"status": "delivered",
"location": "New York, NY",
"description": "Delivered, left at front door"
},
{
"timestamp": "2026-02-05T09:15:00-05:00",
"status": "out_for_delivery",
"location": "New York, NY",
"description": "Out for delivery"
}
],
"lastUpdated": "2026-02-05T14:30:00-05:00"
},
"status": "delivered",
"jobId": "job_abc123xyz",
"createdAt": "2026-02-05T10:00:00.000Z"
},
"clientId:order-002": {
"source": "bulk",
"data": {
"deliveryStatus": "in_transit",
"events": [
{
"timestamp": "2026-02-05T12:00:00-05:00",
"status": "in_transit",
"location": "Chicago, IL",
"description": "In transit to next facility"
}
],
"lastUpdated": "2026-02-05T12:00:00-05:00"
},
"status": "in_transit",
"jobId": "job_def456uvw",
"createdAt": "2026-02-05T10:00:00.000Z"
}
}
} Error Response (401)
Authentication failed
Response Body
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
}
}