> ## Documentation Index
> Fetch the complete documentation index at: https://playwave.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# DELETE /session/end

> Terminate a game session and stop billing

<Badge color="red">DELETE</Badge> `/v1/game/session/end`

Call when a player leaves the game. Stops billing and cleans up the session.

## Request

### Headers

<ParamField header="X-Api-Key" type="string" required>
  Per-game API Key.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

### Body

<ParamField body="game_session_id" type="string" required>
  Game session ID to terminate.
</ParamField>

<ParamField body="play_duration_sec" type="integer">
  Client-measured play time (seconds). Optional — for comparison with the server-side value.
</ParamField>

```json theme={null}
{
  "game_session_id": "gs_550e8400-e29b-41d4-a716-446655440000",
  "play_duration_sec": 1795
}
```

## Response

### Success — <Badge color="green">200</Badge>

```json theme={null}
{
  "success": true,
  "request_id": "req_abc123",
  "data": {
    "result": "SUCCESS",
    "play_duration_sec": 1800,
    "client_play_duration_sec": 1795
  }
}
```

<ResponseField name="result" type="string" required>
  Always `"SUCCESS"`.
</ResponseField>

<ResponseField name="play_duration_sec" type="integer" required>
  **Server-side** play time (seconds) — this is the authoritative value used for settlement.
</ResponseField>

<ResponseField name="client_play_duration_sec" type="integer | null">
  The `play_duration_sec` value sent in the request. `null` if not provided.
</ResponseField>

### Errors

| HTTP                              | Code                | Description                                 | Action |
| --------------------------------- | ------------------- | ------------------------------------------- | ------ |
| <Badge color="yellow">404</Badge> | `SESSION_NOT_FOUND` | Session not found (already expired/deleted) | Ignore |
| <Badge color="orange">409</Badge> | `ALREADY_ENDED`     | Session already ended                       | Ignore |

<Note>
  404 and 409 errors mean the session is already cleaned up — safe to ignore. Just log and continue normal flow.
</Note>

## Luau example

```lua theme={null}
local function endSession(gameSessionId, playDurationSec)
    local requestBody = {
        game_session_id = gameSessionId,
    }

    if playDurationSec then
        requestBody.play_duration_sec = playDurationSec
    end

    local success, response = pcall(function()
        return HttpService:RequestAsync({
            Url = API_URL .. "/game/session/end",
            Method = "DELETE",
            Headers = {
                ["X-Api-Key"] = API_KEY,
                ["Content-Type"] = "application/json",
            },
            Body = HttpService:JSONEncode(requestBody),
        })
    end)

    if not success then
        warn("[Playwave] End session failed:", response)
        return
    end

    -- 404/409 means already cleaned up — ignore
    if response.StatusCode == 404 or response.StatusCode == 409 then
        return
    end

    local body = HttpService:JSONDecode(response.Body)
    return body.data
end
```

## When to call

### PlayerRemoving

Call when an individual player leaves.

```lua theme={null}
Players.PlayerRemoving:Connect(function(player)
    local session = activeSessions[player.UserId]
    if not session then return end

    activeSessions[player.UserId] = nil
    endSession(session.gameSessionId)
end)
```

### BindToClose

Clean up all active sessions on server shutdown.

```lua theme={null}
game:BindToClose(function()
    for userId, session in pairs(activeSessions) do
        activeSessions[userId] = nil
        pcall(function()
            endSession(session.gameSessionId)
        end)
    end
end)
```

<Tip>
  `BindToClose` has a 30-second time limit. Even if HTTP calls fail, sessions are automatically cleaned up via <Tooltip tip="Time-To-Live — Redis automatically deletes the session key after 4 minutes without heartbeat">TTL</Tooltip> expiration after 4 minutes.
</Tip>
