Webhooks Reference¶
Webhook helpers live in erlc_api.webhooks, not top-level erlc_api.
Install signature verification support:
Use explicit imports:
Reference: ER:LC API docs
For a higher-level framework-neutral router dedicated to in-game custom commands, use Custom Commands Reference.
Signature Verification¶
extract_webhook_signature_headers¶
Signature:
Purpose: extract X-Signature-Timestamp and X-Signature-Ed25519.
Return type: WebhookSignatureHeaders.
Common mistakes:
- Renaming headers before verification.
- Treating missing headers as an unsupported event instead of invalid request authentication.
assert_valid_event_webhook_signature¶
Signature:
assert_valid_event_webhook_signature(
*,
raw_body: bytes | bytearray | memoryview,
headers: Mapping[str, Any] | WebhookSignatureHeaders,
public_key_b64: str = PRC_EVENT_WEBHOOK_PUBLIC_KEY_SPKI_B64,
max_skew_s: float | None = 300,
now_epoch_s: float | None = None,
) -> None
Purpose: validate PRC Ed25519 webhook signatures and raise on failure.
Return type: None.
Minimal example:
from erlc_api.webhooks import assert_valid_event_webhook_signature
raw_body = await request.body()
assert_valid_event_webhook_signature(raw_body=raw_body, headers=request.headers)
Important options:
raw_bodymust be the exact bytes received by your web framework.max_skew_s=Nonedisables timestamp skew checking.public_key_b64can be overridden for tests.
Common mistakes:
- Verifying re-serialized JSON instead of the raw request body.
- Catching all exceptions and returning
200; invalid signatures should return non-2xx.
verify_event_webhook_signature¶
Signature:
Purpose: boolean wrapper around assert_valid_event_webhook_signature.
Minimal example:
Common mistake: using the boolean helper when you need an error reason. Use the asserting helper for detailed errors.
Payload Decoding¶
parse_custom_command_text¶
Signature:
Purpose: parse custom command text like ;warn "Player One" RDM.
Return type: CustomCommandInvocation | None.
Minimal example:
from erlc_api.webhooks import parse_custom_command_text
command = parse_custom_command_text(';warn "Player One" RDM')
print(command.command_name, command.args)
Common mistakes:
- Expecting a command when the prefix does not match. The function returns
None.
decode_event_webhook_payload¶
Signature:
decode_event_webhook_payload(payload: Mapping[str, Any], *, command_prefix: str = ";") -> WebhookEvent
Purpose: decode raw webhook JSON into a typed WebhookEvent using explicit
event type fields plus fallback heuristics.
Return type: WebhookEvent.
Minimal example:
from erlc_api.webhooks import decode_event_webhook_payload
event = decode_event_webhook_payload(payload, command_prefix=";")
print(event.event_type)
Common mistakes:
- Assuming a fixed PRC payload shape. The decoder preserves
event.raw.
Event Types And Models¶
WebhookEventType¶
Values:
| Value | Meaning |
|---|---|
WebhookEventType.CUSTOM_COMMAND |
Payload looks like a custom command. |
WebhookEventType.EMERGENCY_CALL |
Payload looks like an emergency call. |
WebhookEventType.UNKNOWN |
Payload could not be classified. |
WebhookSignatureHeaders¶
Fields:
| Field | Type |
|---|---|
timestamp |
str |
signature_hex |
str |
CustomCommandInvocation¶
Fields:
| Field | Type |
|---|---|
raw_text |
str |
prefix |
str |
command_name |
str |
args |
tuple[str, ...] |
command_text |
str |
Helper:
| Helper | Return type | Purpose |
|---|---|---|
.command_key |
str |
Lowercase command name for routing. |
WebhookEvent¶
Fields:
| Field | Type |
|---|---|
event_type |
WebhookEventType |
raw |
Mapping[str, Any] |
event_name |
str | None |
command |
CustomCommandInvocation | None |
emergency_call |
Mapping[str, Any] | None |
EventWebhookRouter¶
Signature:
EventWebhookRouter(
*,
command_prefix: str = ";",
case_sensitive_commands: bool = False,
raise_on_unsupported: bool = False,
)
Purpose: dispatch decoded webhook events to sync or async handlers.
Methods:
| Method | Return type | Purpose |
|---|---|---|
on_command(name, handler=None) |
router or decorator | Register custom command handler. |
on_emergency_call(handler=None) |
router or decorator | Register emergency call handler. |
on_unknown(handler=None) |
router or decorator | Register fallback handler. |
decode(payload) |
WebhookEvent |
Decode payload with router prefix. |
dispatch(payload_or_event) |
list[Any] |
Dispatch to matching handlers. |
Minimal example:
from erlc_api.webhooks import EventWebhookRouter
router = EventWebhookRouter(command_prefix=";")
@router.on_command("ping")
def ping(command, event):
return {"reply": "pong", "args": list(command.args)}
@router.on_emergency_call()
async def emergency(event):
return {"call": event.emergency_call}
@router.on_unknown()
def unknown(event):
return {"ignored": event.event_type}
results = await router.dispatch(payload)
Important options:
case_sensitive_commands=Truekeeps command handler names case-sensitive.raise_on_unsupported=TrueraisesUnsupportedWebhookEventErrorwhen no handler matches and no unknown handler is registered.- Handlers may be sync functions or async functions.
Common mistakes:
- Forgetting parentheses on
@router.on_emergency_call()and@router.on_unknown(). - Registering
pingbut using a differentcommand_prefixthan the server sends.
FastAPI Example¶
from fastapi import FastAPI, HTTPException, Request
from erlc_api.webhooks import EventWebhookRouter, WebhookError, assert_valid_event_webhook_signature
app = FastAPI()
router = EventWebhookRouter(command_prefix=";")
@router.on_command("ping")
def ping(command, event):
return {"reply": "pong"}
@app.post("/erlc/events")
async def erlc_events(request: Request):
raw_body = await request.body()
try:
assert_valid_event_webhook_signature(raw_body=raw_body, headers=request.headers)
except WebhookError as exc:
raise HTTPException(status_code=401, detail=str(exc)) from exc
payload = await request.json()
return {"results": await router.dispatch(payload)}
Webhook Errors¶
| Exception | Raised when |
|---|---|
WebhookError |
Base webhook error. |
MissingWebhookHeaderError |
Required signature header is missing. |
InvalidWebhookSignatureError |
Timestamp, public key, hex signature, or Ed25519 verification fails. |
UnsupportedWebhookEventError |
Router cannot dispatch and raise_on_unsupported=True. |
Related Pages¶
Previous Page: Waiters and Watchers | Next Page: Event Webhooks and Custom Commands