{
"id": "evt_abc123",
"type": "transfer.completed",
"data": {
"sideshiftAccountId": "acct_a1b2c3d4e5f6",
"externalId": "usr_123",
"amountCents": 5000,
"feeCents": 0,
"netAmountCents": 5000,
"currency": "usd",
"paymentId": "tr_provider_123",
"status": "completed",
"metadata": {
"transferId": "txfr_abc123def456",
"direction": "company_to_user",
"destinationBalance": "withdrawal",
"fromAccountId": "company",
"toAccountId": "acct_a1b2c3d4e5f6"
}
},
"timestamp": "2026-03-13T12:00:01.000Z"
}Sent when a transfer completes successfully (any direction: company→user, user→company, user→user).
Verifying Signatures
Every webhook includes x-sideshift-signature and x-sideshift-timestamp headers. Your webhook secret is provided when you configure webhooks in Settings → Connect.
Compute the expected signature as HMAC-SHA256(secret, timestamp.rawPayload) and compare using a constant-time comparison.
const crypto = require("crypto");
function verify(payload, timestamp, signature, secret) {
// Reject old webhooks to prevent replay attacks
if (Math.floor(Date.now() / 1000) - parseInt(timestamp) > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${payload}`)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
Python:
import hmac, hashlib, time
def verify(payload, timestamp, signature, secret):
if int(time.time()) - int(timestamp) > 300:
return False
expected = hmac.new(secret.encode(), f"{timestamp}.{payload}".encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, expected)
Reliability
- Return a
200within 5 seconds to acknowledge receipt. Do heavy processing asynchronously. - A non-2xx response is retried up to three times in one dispatch (1 s, then 2 s apart); anything still failing is visible in
GET /webhook-logsand can be re-sent withPOST /webhooks/{eventId}/replay. - Webhooks may be delivered more than once — deduplicate by event ID.
- Sandbox mode delivers webhooks identically to production.
WEBHOOK
transfer.completed
{
"id": "evt_abc123",
"type": "transfer.completed",
"data": {
"sideshiftAccountId": "acct_a1b2c3d4e5f6",
"externalId": "usr_123",
"amountCents": 5000,
"feeCents": 0,
"netAmountCents": 5000,
"currency": "usd",
"paymentId": "tr_provider_123",
"status": "completed",
"metadata": {
"transferId": "txfr_abc123def456",
"direction": "company_to_user",
"destinationBalance": "withdrawal",
"fromAccountId": "company",
"toAccountId": "acct_a1b2c3d4e5f6"
}
},
"timestamp": "2026-03-13T12:00:01.000Z"
}Authorizations
Your SideShift Connect API key (sk_live_* or sk_test_*). Generate from Settings → Connect.
Body
application/json
Response
200
Webhook acknowledged