POST /v2/webhooks/register

웹훅 등록 (과금 대상)

개요

택배 추적을 위한 웹훅을 등록합니다. 2가지 모드를 지원합니다:

  • recurring: true - 배송 완료까지 계속 모니터링 (구독형)
  • recurring: false - 1회만 조회 (일회성, 기본값) 과금 대상 API이며, 최대 500건까지 한 번에 등록 가능합니다.

요청 예제

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))
}
💡 참고: 테스트 API 키를 대시보드에서 발급받은 실제 API 키로 교체하세요.

요청 본문

Name Type Required Description
trackingItems array Required
추적 항목 (최대 500개)
trackingItems[].carrier string Required
trackingItems[].trackingNumber string Required
trackingItems[].clientId string Optional
recurring boolean Optional
⭐ 추적 모드 (선택적, 기본값: false) - false: 1회만 조회 (기본값, 안전함) → webhookEndpointId 선택적 - true: 계속 모니터링 (명시적 요청 필요) → webhookEndpointId 필수
webhookEndpointId string Optional
사전 등록된 Webhook Endpoint ID - recurring: true일 때 필수 (미들웨어 검증) - recurring: false일 때 선택적 - 있으면 완료 시 POST 전송, 없으면 전송 안함 - 먼저 Webhook Endpoint를 등록해야 함

응답

성공 응답 (200)

200 OK

웹훅 등록 성공

Response Body

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

오류 응답 (401)

401 Unauthorized

인증 실패

Response Body

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

오류 응답 (429)

429 Too Many Requests

요청 한도 초과

Response Body

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

웹훅 구독 목록 조회

개요

등록한 모든 웹훅 구독(구독형 + 일회성)을 조회합니다. 무료 API로 사용량이 차감되지 않습니다.

요청 예제

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))
}
💡 참고: 테스트 API 키를 대시보드에서 발급받은 실제 API 키로 교체하세요.

응답

성공 응답 (200)

200 OK

구독 목록 조회 성공

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"
  }
]

오류 응답 (401)

401 Unauthorized

인증 실패

Response Body

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

단일 웹훅 구독 조회

개요

특정 requestId의 웹훅 구독 정보를 조회합니다. 무료 API로 사용량이 차감되지 않습니다.

요청 예제

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))
}
💡 참고: 테스트 API 키를 대시보드에서 발급받은 실제 API 키로 교체하세요.

경로 매개변수

Name Type Required Description
requestId string Required
<p>웹훅 등록 시 받은 requestId</p>
Example: req_abc123xyz

응답

성공 응답 (200)

200 OK

구독 정보 조회 성공

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"
  }
}

오류 응답 (401)

401 Unauthorized

인증 실패

Response Body

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

오류 응답 (404)

404 Not Found

requestId를 찾을 수 없음

Response Body

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

웹훅 구독 삭제

개요

웹훅 구독을 취소하고 모니터링을 중지합니다. 구독형(recurring) 웹훅의 경우 즉시 중지됩니다. 무료 API로 사용량이 차감되지 않습니다.

요청 예제

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))
}
💡 참고: 테스트 API 키를 대시보드에서 발급받은 실제 API 키로 교체하세요.

경로 매개변수

Name Type Required Description
requestId string Required
<p>삭제할 웹훅의 requestId</p>
Example: req_abc123xyz

응답

성공 응답 (200)

200 OK

구독 삭제 성공

Response Body

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

오류 응답 (401)

401 Unauthorized

인증 실패

Response Body

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

오류 응답 (404)

404 Not Found

requestId를 찾을 수 없음

Response Body

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

웹훅 변경 이력 조회

개요

특정 웹훅 구독의 모든 변경 이력(배송 상태 변경)을 조회합니다. 무료 API로 사용량이 차감되지 않습니다.

요청 예제

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))
}
💡 참고: 테스트 API 키를 대시보드에서 발급받은 실제 API 키로 교체하세요.

경로 매개변수

Name Type Required Description
requestId string Required
<p>웹훅 등록 시 받은 requestId</p>
Example: req_abc123xyz

응답

성공 응답 (200)

200 OK

변경 이력 조회 성공

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
  }
}

오류 응답 (401)

401 Unauthorized

인증 실패

Response Body

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

오류 응답 (404)

404 Not Found

requestId를 찾을 수 없음

Response Body

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

단일 변경 이벤트 조회

개요

특정 변경 이벤트의 상세 정보를 조회합니다. 무료 API로 사용량이 차감되지 않습니다.

요청 예제

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))
}
💡 참고: 테스트 API 키를 대시보드에서 발급받은 실제 API 키로 교체하세요.

경로 매개변수

Name Type Required Description
requestId string Required
<p>웹훅 등록 시 받은 requestId</p>
Example: req_abc123xyz
changeId string Required
<p>변경 이벤트 ID</p>
Example: change_def456

응답

성공 응답 (200)

200 OK

변경 이벤트 조회 성공

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"
}

오류 응답 (401)

401 Unauthorized

인증 실패

Response Body

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

오류 응답 (404)

404 Not Found

requestId 또는 changeId를 찾을 수 없음

Response Body

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

웹훅 결과 배치 검색

개요

여러 택배의 웹훅 결과를 한 번에 검색합니다. 등록한 웹훅의 최종 결과를 조회할 때 사용합니다.

검색 방식:

  • carrier + trackingNumber로 검색
  • clientId로 검색
  • 두 방식을 동시에 사용 가능

동작 방식:

  • tracking_requests 컬렉션에서 trackingIndex/clientIdIndex 필드를 사용하여 검색
  • 같은 송장번호가 여러 문서에 있을 경우 최신 문서만 반환
  • Firestore array-contains-any 제한(10개)으로 인해 배치 처리 (최대 100개 조회 시 10번 쿼리)

무료 API로 사용량이 차감되지 않습니다.

요청 예제

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))
}
💡 참고: 테스트 API 키를 대시보드에서 발급받은 실제 API 키로 교체하세요.

요청 본문

Name Type Required Description
trackingItems array Required

응답

성공 응답 (200)

200 OK

배치 검색 성공

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"
    }
  }
}

오류 응답 (401)

401 Unauthorized

인증 실패

Response Body

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