LTESocks sends webhook events to your configured endpoint as an HTTP POST request with a JSON body. Use webhooks to react to port lifecycle and connectivity changes without polling.
Create a webhook
Section titled “Create a webhook”Create a new webhook in your LTESocks user dashboard. Enter the final public HTTPS endpoint that will receive events, then copy the signing secret into your server-side secret store. Do not expose the secret in browser code, logs, or client applications.
Send a test event if the dashboard offers one, or verify the next real delivery before relying on the integration. For reset completion, subscribe to port.ip_changed and follow the sequence in Port reset and readiness.
HTTP request
Section titled “HTTP request”Every delivery uses this shape:
POST /your-webhook-path HTTP/1.1Content-Type: application/jsonX-Signature: BASE64_HMAC_SHA256The signature is calculated from the exact request body. No bearer token is sent with a webhook.
Event envelope
Section titled “Event envelope”All events use a versioned envelope:
{ "id": "66c5f99e2f66247a9284620f", "version": "v1", "type": "port.ip_changed", "occurred_at": "2026-08-26T09:15:37.123Z", "data": { "port": { "id": "66bca5147010246361dbe256", "service_id": "service-123", "number": 1042, "status": "active" }, "modem": { "id": "66bca4d67010246361dbe213", "name": "modem-kyiv-01" }, "old_ip": "198.51.100.10", "new_ip": "203.0.113.24" }}| Field | Type | Meaning |
|---|---|---|
id | string | Unique event identifier. Use it as an idempotency key. |
version | string | Payload contract version. The current value is v1. |
type | string | Event type. |
occurred_at | string | Event time in UTC, encoded as RFC 3339. |
data.port | object | Port associated with the event. Always present. |
data.modem | object | Modem details when the event is associated with a modem. |
data.old_ip | string | Previous IP address for port.ip_changed. |
data.new_ip | string | New IP address for port.ip_changed. |
The port object contains id, service_id, number, and status. A modem object contains id and name; last_seen_at is included for modem disconnection events.
Clients should ignore unknown fields so that compatible additions do not break event processing.
Event types
Section titled “Event types”port.expired
Section titled “port.expired”Sent when a port expires:
{ "id": "66c5f99e2f66247a92846210", "version": "v1", "type": "port.expired", "occurred_at": "2026-08-26T09:20:00Z", "data": { "port": { "id": "66bca5147010246361dbe256", "service_id": "service-123", "number": 1042, "status": "disabled" } }}port.ip_changed
Section titled “port.ip_changed”Sent when the public IP address of the modem serving a port changes. The payload includes modem, old_ip, and new_ip:
{ "id": "66c5f99e2f66247a92846211", "version": "v1", "type": "port.ip_changed", "occurred_at": "2026-08-26T09:21:00Z", "data": { "port": { "id": "66bca5147010246361dbe256", "service_id": "service-123", "number": 1042, "status": "active" }, "modem": { "id": "66bca4d67010246361dbe213", "name": "modem-kyiv-01" }, "old_ip": "198.51.100.10", "new_ip": "203.0.113.24" }}port.modem_disconnected
Section titled “port.modem_disconnected”Sent when the modem serving a port disconnects. last_seen_at is the modem's last recorded activity time:
{ "id": "66c5f99e2f66247a92846212", "version": "v1", "type": "port.modem_disconnected", "occurred_at": "2026-08-26T09:22:00Z", "data": { "port": { "id": "66bca5147010246361dbe256", "service_id": "service-123", "number": 1042, "status": "disconnected" }, "modem": { "id": "66bca4d67010246361dbe213", "name": "modem-kyiv-01", "last_seen_at": "2026-08-26T09:21:45Z" } }}Verify the signature
Section titled “Verify the signature”LTESocks computes:
Base64(HMAC-SHA256(webhook_secret, raw_request_body))The result is sent in X-Signature. Keep the whsec_... secret server-side and compare signatures with a constant-time function.
Node.js
Section titled “Node.js”import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWebhook(rawBody, receivedSignature, secret) { const expected = createHmac('sha256', secret) .update(rawBody) .digest('base64');
const received = Buffer.from(receivedSignature ?? '', 'base64'); const calculated = Buffer.from(expected, 'base64');
return ( received.length === calculated.length && timingSafeEqual(received, calculated) );}<?php$rawBody = file_get_contents('php://input');$received = $_SERVER['HTTP_X_SIGNATURE'] ?? '';$expected = base64_encode(hash_hmac('sha256', $rawBody, $webhookSecret, true));
if (!hash_equals($expected, $received)) { http_response_code(401); exit;}Official SDK
Section titled “Official SDK”The official libraries verify the signature in constant time and decode JSON only after verification succeeds:
import { WebhookVerifier } from '@ltesocks/sdk';
const event = await WebhookVerifier.decodeVerifiedJson( rawRequestBody, request.headers.get('x-signature') ?? '', process.env.LTESOCKS_WEBHOOK_SECRET,);import os
from ltesocks_sdk import WebhookVerifier
event = WebhookVerifier.decode_verified_json( raw_body, signature, os.environ["LTESOCKS_WEBHOOK_SECRET"],)<?php
use LTESocks\Webhook\WebhookVerifier;
$event = WebhookVerifier::decodeVerifiedJson( $rawBody, $signature, (string) getenv('LTESOCKS_WEBHOOK_SECRET'),);Process deliveries safely
Section titled “Process deliveries safely”- Preserve the raw body and verify
X-Signature. - Reject unsupported
versionvalues. - Store
idbefore starting side effects. - If
idwas already processed, return2xxwithout repeating the side effect. - Enqueue the event for asynchronous processing.
- Return a
2xxresponse promptly.
LTESocks treats any 2xx response as successful. The response body is ignored. Redirects are not followed.
Retries and timeouts
Section titled “Retries and timeouts”Delivery may be retried after DNS or network errors, timeouts, HTTP 408, HTTP 429, and 5xx responses. Redirects, TLS validation errors, endpoint policy errors, and other 4xx responses are not retried.
The default delivery policy is:
| Setting | Default |
|---|---|
| Read timeout | 5 seconds |
| Write and connection timeout | 5 seconds |
| Retries after the initial request | 3 |
| Delay between attempts | 10 seconds |
With the default policy, one event can be delivered up to four times. Each retry uses the same body, event id, and signature, so receivers must be idempotent.
Endpoint requirements
Section titled “Endpoint requirements”- Use an
httporhttpsURL; HTTPS with TLS 1.2 or newer is recommended. - The hostname must resolve only to public IP addresses.
- Localhost, private, link-local, multicast, and reserved addresses are rejected.
- Embedded URL credentials and fragments are not allowed.
- Do not depend on redirects; configure the final URL directly.