POST /v2/webhooks/register

Register webhook (billable)

Overview

Register a webhook for parcel tracking. Supports 2 modes:

  • recurring: true - Continuous monitoring until delivery (subscription)
  • recurring: false - Query once only (one-time, default) Billable API, supports up to 500 items per registration.

Example Request

curl -X POST https://api.whereparcel.com/v2/webhooks/register \
  -H "Authorization: Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production" \
  -H "Content-Type: application/json" \
  -d '{
    "trackingItems": [
      {
        "carrier": "kr.cj",
        "trackingNumber": "123456789012",
        "clientId": "order-001"
      }
    ],
    "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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
      "trackingItems": [
        {
          "carrier": "kr.cj",
          "trackingNumber": "123456789012",
          "clientId": "order-001"
        }
      ],
      "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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
  'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$data = [
  'trackingItems' => [
    '0' => [
      'carrier' => "kr.cj",
      'trackingNumber' => "123456789012",
      'clientId' => "order-001"
    ]
  ],
  '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
    'Content-Type': 'application/json'
}

data = {
    'trackingItems': {
        '0': {
            'carrier': "kr.cj",
            'trackingNumber': "123456789012",
            'clientId': "order-001"
        }
    },
    '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":"kr.cj","trackingNumber":"123456789012","clientId":"order-001"}],"recurring":true,"webhookEndpointId":"endpoint-id-123"}`)
	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))

	req.Header.Add("Authorization", "Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production")
	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))
}
💡 Note: 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 500)
trackingItems[].carrier string Required
trackingItems[].trackingNumber string Required
trackingItems[].clientId 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)

200 OK

Webhook registered successfully

Response Body

{
  "success": true,
  "requestId": "req_abc123xyz",
  "subscriptionId": "sub_def456uvw",
  "status": "active",
  "message": "Webhook registered successfully (recurring mode)"
}

Error Response (401)

401 Unauthorized

Authentication failed

Response Body

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Error Response (429)

429 Too Many Requests

Rate limit exceeded

Response Body

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please try again later."
  }
}
GET /v2/webhooks/subscriptions

List webhook subscriptions

Overview

Retrieve all registered webhook subscriptions (recurring + one-time). Free API - does not count towards usage quota.

Example Request

curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions \
  -H "Authorization: Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production" \
  -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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production",
    "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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
  '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
    '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production")
	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))
}
💡 Note: Replace the test API key with your actual API key from the dashboard.

Response

Success Response (200)

200 OK

Subscription list retrieved successfully

Response Body

[
  {
    "requestId": "req_abc123xyz",
    "trackingItemCount": 2,
    "trackingItems": [
      {
        "carrier": "kr.cj",
        "trackingNumber": "300718039335",
        "clientId": "order-001",
        "latestStatus": "delivered"
      },
      {
        "carrier": "kr.lotte",
        "trackingNumber": "410259694563",
        "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": "kr.post",
        "trackingNumber": "1234567890123",
        "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)

401 Unauthorized

Authentication failed

Response Body

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}
GET /v2/webhooks/subscriptions/{requestId}

Get webhook subscription

Overview

Retrieve webhook subscription information for a specific requestId. Free API - does not count towards usage quota.

Example Request

curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId} \
  -H "Authorization: Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production" \
  -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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production",
    "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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
  '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
    '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production")
	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))
}
💡 Note: 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)

200 OK

Subscription info retrieved successfully

Response Body

{
  "success": true,
  "data": {
    "requestId": "req_abc123xyz",
    "trackingItemCount": 1,
    "trackingItems": [
      {
        "carrier": "kr.cj",
        "trackingNumber": "123456789012",
        "latestStatus": "in_transit"
      }
    ],
    "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)

401 Unauthorized

Authentication failed

Response Body

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Error Response (404)

404 Not Found

requestId not found

Response Body

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested resource was not found"
  }
}
DELETE /v2/webhooks/subscriptions/{requestId}

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

curl -X DELETE https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId} \
  -H "Authorization: Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production" \
  -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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production",
    "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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
  '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
    '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production")
	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))
}
💡 Note: 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)

200 OK

Subscription deleted successfully

Response Body

{
  "deleted": true,
  "requestId": "req_abc123xyz",
  "message": "Webhook subscription deleted successfully"
}

Error Response (401)

401 Unauthorized

Authentication failed

Response Body

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Error Response (404)

404 Not Found

requestId not found

Response Body

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested resource was not found"
  }
}
GET /v2/webhooks/subscriptions/{requestId}/changes

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

curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes \
  -H "Authorization: Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production" \
  -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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production",
    "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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
  '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
    '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production")
	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))
}
💡 Note: 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)

200 OK

Change history retrieved successfully

Response Body

{
  "events": [
    {
      "eventId": "evt_abc123",
      "requestId": "req_abc123xyz",
      "webhookUrl": "https://myapp.com/webhooks/whereparcel",
      "event": "status.changed",
      "payload": {
        "trackingNumber": "300718039335",
        "carrier": {
          "code": "kr.cj",
          "name": "CJ대한통운"
        },
        "previousStatus": "in_transit",
        "currentStatus": "out_for_delivery",
        "trackingData": {
          "carrier": "kr.cj",
          "carrierName": "CJ대한통운",
          "trackingNumber": "300718039335",
          "status": "out_for_delivery",
          "statusText": "배달 출발",
          "events": [
            {
              "timestamp": "2026-02-05T09:15:00+09:00",
              "status": "out_for_delivery",
              "statusText": "배달 출발",
              "location": "서울 강남구 센터"
            }
          ]
        },
        "timestamp": "2026-02-05T09:15:00+09:00"
      },
      "status": "delivered",
      "deliveryAttempts": 1,
      "createdAt": "2026-02-05T09:15:00.000Z",
      "updatedAt": "2026-02-05T09:16:00.000Z"
    },
    {
      "eventId": "evt_def456",
      "requestId": "req_abc123xyz",
      "webhookUrl": "https://myapp.com/webhooks/whereparcel",
      "event": "status.delivered",
      "payload": {
        "trackingNumber": "300718039335",
        "carrier": {
          "code": "kr.cj",
          "name": "CJ대한통운"
        },
        "previousStatus": "out_for_delivery",
        "currentStatus": "delivered",
        "trackingData": {
          "carrier": "kr.cj",
          "carrierName": "CJ대한통운",
          "trackingNumber": "300718039335",
          "status": "delivered",
          "statusText": "배달완료",
          "events": [
            {
              "timestamp": "2026-02-05T14:30:00+09:00",
              "status": "delivered",
              "statusText": "배달완료",
              "location": "서울 강남구"
            }
          ]
        },
        "timestamp": "2026-02-05T14:30:00+09:00"
      },
      "status": "delivered",
      "deliveryAttempts": 1,
      "createdAt": "2026-02-05T14:30:00.000Z",
      "updatedAt": "2026-02-05T14:31:00.000Z"
    }
  ],
  "pagination": {
    "limit": 20,
    "nextCursor": "cursor_xyz789",
    "hasMore": false
  }
}

Error Response (401)

401 Unauthorized

Authentication failed

Response Body

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Error Response (404)

404 Not Found

requestId not found

Response Body

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested resource was not found"
  }
}
GET /v2/webhooks/subscriptions/{requestId}/changes/{changeId}

Get single change event

Overview

Retrieve detailed information for a specific change event. Free API - does not count towards usage quota.

Example Request

curl -X GET https://api.whereparcel.com/v2/webhooks/subscriptions/{requestId}/changes/{changeId} \
  -H "Authorization: Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production" \
  -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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production",
    "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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
  '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
    '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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production")
	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))
}
💡 Note: 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)

200 OK

Change event retrieved successfully

Response Body

{
  "eventId": "evt_abc123",
  "requestId": "req_abc123xyz",
  "webhookUrl": "https://myapp.com/webhooks/whereparcel",
  "event": "status.changed",
  "payload": {
    "trackingNumber": "300718039335",
    "carrier": {
      "code": "kr.cj",
      "name": "CJ대한통운"
    },
    "previousStatus": "in_transit",
    "currentStatus": "out_for_delivery",
    "trackingData": {
      "carrier": "kr.cj",
      "carrierName": "CJ대한통운",
      "trackingNumber": "300718039335",
      "status": "out_for_delivery",
      "statusText": "배달 출발",
      "events": [
        {
          "timestamp": "2026-02-05T09:15:00+09:00",
          "status": "out_for_delivery",
          "statusText": "배달 출발",
          "location": "서울 강남구 센터"
        },
        {
          "timestamp": "2026-02-05T08:00:00+09:00",
          "status": "in_transit",
          "statusText": "배송 중",
          "location": "서울 송파구 센터"
        }
      ]
    },
    "timestamp": "2026-02-05T09:15:00+09:00"
  },
  "status": "delivered",
  "deliveryAttempts": 1,
  "maxAttempts": 3,
  "createdAt": "2026-02-05T09:15:00.000Z",
  "updatedAt": "2026-02-05T09:16:00.000Z"
}

Error Response (401)

401 Unauthorized

Authentication failed

Response Body

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Error Response (404)

404 Not Found

requestId or changeId not found

Response Body

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "The requested resource was not found"
  }
}
POST /v2/webhooks/results

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

curl -X POST https://api.whereparcel.com/v2/webhooks/results \
  -H "Authorization: Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production" \
  -H "Content-Type: application/json" \
  -d '{
    "trackingItems": [
      {
        "carrier": "kr.cj",
        "trackingNumber": "300718039335"
      },
      {
        "carrier": "kr.lotte",
        "trackingNumber": "410259694563"
      }
    ]
  }'
const response = await fetch('https://api.whereparcel.com/v2/webhooks/results', {
  method: 'POST',
  headers: {
    "Authorization": "Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
      "trackingItems": [
        {
          "carrier": "kr.cj",
          "trackingNumber": "300718039335"
        },
        {
          "carrier": "kr.lotte",
          "trackingNumber": "410259694563"
        }
      ]
    })
});

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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
  'Content-Type: application/json'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$data = [
  'trackingItems' => [
    '0' => [
      'carrier' => "kr.cj",
      'trackingNumber' => "300718039335"
    ],
    '1' => [
      'carrier' => "kr.lotte",
      'trackingNumber' => "410259694563"
    ]
  ]
];
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_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production',
    'Content-Type': 'application/json'
}

data = {
    'trackingItems': {
        '0': {
            'carrier': "kr.cj",
            'trackingNumber': "300718039335"
        },
        '1': {
            'carrier': "kr.lotte",
            'trackingNumber': "410259694563"
        }
    }
}

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":"kr.cj","trackingNumber":"300718039335"},{"carrier":"kr.lotte","trackingNumber":"410259694563"}]}`)
	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))

	req.Header.Add("Authorization", "Bearer wp_test_public_demo_key_do_not_use_in_production:sk_test_public_demo_secret_do_not_use_in_production")
	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))
}
💡 Note: 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)

200 OK

Batch search completed successfully

Response Body

{
  "results": {
    "kr.cj:300718039335": {
      "source": "webhook",
      "data": {
        "carrier": "kr.cj",
        "carrierName": "CJ대한통운",
        "trackingNumber": "300718039335",
        "status": "delivered",
        "statusText": "배달완료",
        "events": [
          {
            "timestamp": "2026-02-05T14:30:00+09:00",
            "status": "delivered",
            "statusText": "배달완료",
            "location": "서울 강남구"
          },
          {
            "timestamp": "2026-02-05T09:15:00+09:00",
            "status": "out_for_delivery",
            "statusText": "배달 출발",
            "location": "서울 강남구 센터"
          }
        ]
      },
      "requestId": "sub_abc123xyz",
      "createdAt": "2026-02-05T10:00:00.000Z"
    },
    "clientId:order-002": {
      "source": "webhook",
      "data": {
        "carrier": "kr.lotte",
        "carrierName": "롯데택배",
        "trackingNumber": "410259694563",
        "status": "in_transit",
        "statusText": "배송 중",
        "events": [
          {
            "timestamp": "2026-02-05T12:00:00+09:00",
            "status": "in_transit",
            "statusText": "배송 중",
            "location": "경기 성남시"
          }
        ]
      },
      "requestId": "sub_def456uvw",
      "createdAt": "2026-02-05T10:00:00.000Z"
    }
  }
}

Error Response (401)

401 Unauthorized

Authentication failed

Response Body

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid API key"
  }
}