---
title: "Safe Events"
description: "Server events your clients can trigger but cheaters cannot."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.asphyxia.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Safe Events

A normal server event is just a name. Anyone can call
`TriggerServerEvent("payroll:givePaycheck", 999999)` from a cheat menu with any
arguments they like, and your server cannot tell it apart from a real player
clicking a button.

A Safe Event can only be triggered by a real, Asphyxia-protected client. This
page covers what the protection guarantees and how to register, handle and
trigger one.

## What it guarantees

- Your real event is never networked, so clients cannot reach it with
  `TriggerServerEvent`. The client only learns a key you choose.
- Triggering goes through an Asphyxia export, and each trigger is tied to the
  player's live [heartbeat](/anticheat/heartbeat) session. No session, no trigger.

Anything that fails on the way — no session, a forged payload, an unregistered
event, or a trigger during cooldown — results in a ban, logged like any other
detection.

> **Note**
>
> Safe Events prove who sent a request, not that they are allowed to make it. Keep
> your own permission and sanity checks in the handler.

## Setup

### 1. Register the event (server)

Call this at the top level of a server script, not inside a thread, callback or
event handler.

```lua
exports.asphyxia:registerProtectedEvent("givePaycheck", {
eventName = "payroll:givePaycheck",
cooldown  = 60 * 1000, -- optional, per player, in ms
})
```

| Parameter | Type | Description |
| :--- | :--- | :--- |
| `eventKey` | `string` | The key your client script uses to trigger it |
| `config.eventName` | `string` | Your real event name. Never sent to the client |
| `config.cooldown` | `number?` | Minimum ms between triggers, per player. Omit for none |

### 2. Handle it (server)

Use `AddEventHandler`, never `RegisterNetEvent`. The first argument is the
verified net ID of the player who triggered it.

```lua
AddEventHandler("payroll:givePaycheck", function(source, amount)
exports.your_framework:AddMoney(source, "bank", amount)
end)
```

> **Danger**
>
> `RegisterNetEvent` is the only thing that makes an event name reachable from the
> network. Calling it on your `eventName` opens an unprotected path straight to
> your handler and defeats the entire system.

### 3. Trigger it (client)

```lua
local events = exports.asphyxia:getEvents()

RegisterCommand("claimpay", function()
exports.asphyxia:triggerSafeEvent(events.givePaycheck, 500)
end)
```

`getEvents()` returns your resource's registered keys, so you do not hardcode
strings. It returns `nil` if the resource registered nothing.

`triggerSafeEvent(eventKey, ...)` takes any number of arguments — strings,
numbers, booleans, tables — and otherwise behaves like `TriggerServerEvent`.

## Notes

- Both client exports can be called as soon as your script starts. They wait for
  the anti-cheat session themselves.
- Cooldowns are enforced per player on the server. Looping `triggerSafeEvent`
  gets the player banned.
- To exempt a player from false positives, see
  [Whitelisting](/anticheat/whitelisting) (`safeEventsIntegrity`).

Source: https://docs.asphyxia.dev/anticheat/safe-events/index.mdx
