Agent skill
eBay Webhooks Skill
Receive and verify eBay Notification API webhooks (Platform Notifications / Event Notifications). Use when setting up an eBay webhook endpoint, passing the endpoint challenge validation, debugging the x-ebay-signature ECDSA verification, fetching the public key with getPublicKey, or handling events like MARKETPLACE_ACCOUNT_DELETION.
Install this skill
npx skills add hookdeck/webhook-skills --skill ebay-webhooks
When to Use This Skill
- How do I receive eBay webhooks (notifications)?
- How do I pass eBay's endpoint challenge validation (
challenge_code)? - How do I verify the
x-ebay-signatureheader (ECDSA)? - How do I use the eBay
getPublicKeyendpoint and cache the public key? - How do I handle
MARKETPLACE_ACCOUNT_DELETION(marketplace account deletion / closure) notifications? - Why is my eBay signature verification failing?
How eBay Webhooks Differ From Most Providers
eBay does not use HMAC with a shared secret, and does not follow the Standard Webhooks spec. Two distinct mechanisms are involved:
- Endpoint challenge (one-time, on save) — When you register or update a destination, eBay sends
GET https://<your-endpoint>?challenge_code=.... You must respond HTTP 200 with JSON{"challengeResponse":"<hex>"}where the hex is the SHA-256 hash of exactly `challengeCode + verificationToken- endpoint` — in that order. The order is mandatory.
- Per-notification signature (ECDSA) — Every notification carries an
x-ebay-signatureheader: a Base64-encoded JSON object with fieldsalg,kid,signature, anddigest. Use thekidto fetch the matching public key viagetPublicKey, then verify the ECDSA signature over the raw request body. Cache the public key ~1 hour (keyed bykid).
Verification (core)
Endpoint challenge — deterministic SHA-256, no crypto keys needed:
const crypto = require('crypto');
function challengeResponse(challengeCode, verificationToken, endpoint) {
// ORDER IS MANDATORY: challengeCode + verificationToken + endpoint
const hash = crypto.createHash('sha256');
hash.update(challengeCode);
hash.update(verificationToken);
hash.update(endpoint);
return hash.digest('hex'); // return as { challengeResponse: <hex> } with HTTP 200
}
Per-notification signature — ECDSA over the raw body, key fetched by kid:
async function verifyEbaySignature(rawBody, signatureHeader, getPublicKey) {
if (!signatureHeader) return false;
let sig;
try { sig = JSON.parse(Buffer.from(signatureHeader, 'base64').toString('utf8')); }
catch { return false; } // { alg, kid, signature, digest }
if (!sig.kid || !sig.signature) return false;
const pem = await getPublicKey(sig.kid); // cache ~1h, keyed by kid
const verifier = crypto.createVerify('sha1'); // eBay signs ECDSA with SHA-1
verifier.update(rawBody); // RAW body bytes — do not re-serialize
verifier.end();
try { return verifier.verify(pem, sig.signature, 'base64'); }
catch { return false; }
}
For complete handlers with the challenge route, the
getPublicKeyfetch + LRU cache, event dispatch, and tests, see:
Official SDK (Node.js): eBay publishes
event-notification-nodejs-sdk, which wraps the exact algorithm above (EventNotificationSDK.process(...)for signatures,validateEndpoint(...)for the challenge). The examples use a transparent manual implementation so they are testable offline without the OAuth call thatgetPublicKeyrequires — see references/verification.md for the SDK path.
Common Event Types (Topics)
| Topic | Triggered When |
|---|---|
MARKETPLACE_ACCOUNT_DELETION | An eBay user closed their account / requested personal-data deletion. All developers must subscribe or opt out. |
AUTHORIZATION_REVOCATION | A user revoked your app's authorization — stop making API calls on their behalf and clean up stored tokens. |
ITEM_AVAILABILITY | Availability of a subscribed item changed |
ITEM_PRICE_REVISION | Price of a subscribed item was revised |
PRIORITY_LISTING_REVISION | A priority listing was revised |
The topic arrives in the payload at metadata.topic. The full, current list of topics (and the OAuth scopes needed to subscribe) is returned by the getTopics method — do not hard-code a list you cannot see.
Environment Variables
EBAY_VERIFICATION_TOKEN=your_verification_token # 32-80 chars, [A-Za-z0-9_-] only
EBAY_ENDPOINT=https://your-domain.com/webhooks/ebay # EXACT public URL eBay calls
EBAY_CLIENT_ID=your_app_id # App credentials (getPublicKey OAuth)
EBAY_CLIENT_SECRET=your_cert_id
EBAY_ENV=production # sandbox | production
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 ebay --path /webhooks/ebay
Use the forwarding URL as EBAY_ENDPOINT and as the destination URL you register with eBay. The endpoint URL used in the SHA-256 challenge hash must be the exact URL eBay calls (the public tunnel URL, not localhost).
Reference Materials
- references/overview.md - eBay notification concepts, topics, payloads
- references/setup.md - Configure destinations, verification token, subscriptions
- references/verification.md - Challenge + ECDSA signature details, SDK path, gotchas