Back to skills
SKILL.md
076 Api 59571440
ASecurity```typescript export default { async tail(events: TraceItem[], env: Env, ctx: ExecutionContext): Promise<void> { // Process events }, } satisfies ExportedHandler<Env> ```
- 9 stars
- 0 votes
- 0 copies
- 0 views
- Added October 11, 2026
Works with
Security analysis
100/100npx -y skills add tools-only/X-Skills --skill 076-api_59571440 --agent claude-codeAre you the author of 076 Api 59571440?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tools-only-076-api-59571440)# Tail Workers API Reference
## Handler Signature
```typescript
export default {
async tail(events: TraceItem[], env: Env, ctx: ExecutionContext): Promise<void> {
// Process events
},
} satisfies ExportedHandler<Env>
```
**Parameters:**
- `events`: Array of `TraceItem` objects (one per producer invocation)
- `env`: Bindings (KV, D1, R2, env vars, etc.)
- `ctx`: Context with `waitUntil()` for async work
**CRITICAL:** Tail handlers don't return values. Use `ctx.waitUntil()` for async operations.
## TraceItem Type
```typescript
interface TraceItem {
scriptName: string // Producer Worker name
eventTimestamp: number // Epoch milliseconds
outcome:
| 'ok'
| 'exception'
| 'exceededCpu'
| 'exceededMemory'
| 'canceled'
| 'scriptNotFound'
| 'responseStreamDisconnected'
| 'unknown'
event?: {
request?: {
url: string // Redacted by default
method: string
headers: Record<string, string> // Sensitive headers redacted
cf?: IncomingRequestCfProperties
getUnredacted(): TraceRequest // Bypass redaction (use carefully)
}
response?: {
status: number
}
}
logs: Array<{
timestamp: number // Epoch milliseconds
level: 'debug' | 'info' | 'log' | 'warn' | 'error'
message: unknown[] // Args passed to console function
}>
exceptions: Array<{
timestamp: number // Epoch milliseconds
name: string // Error type (Error, TypeError, etc.)
message: string // Error description
}>
diagnosticsChannelEvents: Array<{
channel: string
message: unknown
timestamp: number // Epoch milliseconds
}>
}
```
**Note:** Official SDK uses `TraceItem`, not `TailItem`. Use `@cloudflare/workers-types` for accurate types.
## Timestamp Handling
All timestamps are **epoch milliseconds**, not seconds:
```typescript
// ✅ CORRECT - use directly with Date
const date = new Date(event.eventTimestamp)
// ❌ WRONG - don't multiply by 1000
const date = new Date(event.eventTimestamp * 1000)
```
## Automatic Redaction
By default, sensitive data is redacted from `TraceRequest`:
### Header Redaction
Headers containing these substrings (case-insensitive):
- `auth`, `key`, `secret`, `token`, `jwt`
- `cookie`, `set-cookie`
Redacted values show as `"REDACTED"`.
### URL Redaction
- **Hex IDs:** 32+ hex digits → `"REDACTED"`
- **Base-64 IDs:** 21+ chars with 2+ upper, 2+ lower, 2+ digits → `"REDACTED"`
## Bypassing Redaction
```typescript
export default {
async tail(events, env, ctx) {
for (const event of events) {
// ⚠️ Use with extreme caution
const unredacted = event.event?.request?.getUnredacted()
// unredacted.url and unredacted.headers contain raw values
}
},
}
```
**Best practices:**
- Only call `getUnredacted()` when absolutely necessary
- Never log unredacted sensitive data
- Implement additional filtering before external transmission
- Use environment variables for API keys, never hardcode
## Type-Safe Handler
```typescript
interface Env {
LOGS_KV: KVNamespace
ANALYTICS: AnalyticsEngineDataset
LOG_ENDPOINT: string
API_TOKEN: string
}
export default {
async tail(events: TraceItem[], env: Env, ctx: ExecutionContext): Promise<void> {
const payload = events.map((event) => ({
script: event.scriptName,
timestamp: event.eventTimestamp,
outcome: event.outcome,
url: event.event?.request?.url,
status: event.event?.response?.status,
}))
ctx.waitUntil(
fetch(env.LOG_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}),
)
},
} satisfies ExportedHandler<Env>
```
## Outcome vs HTTP Status
**IMPORTANT:** `outcome` is script execution status, NOT HTTP status.
- Worker returns 500 → `outcome='ok'` if script completed successfully
- Uncaught exception → `outcome='exception'` regardless of HTTP status
- CPU limit exceeded → `outcome='exceededCpu'`
```typescript
// ✅ Check outcome for script execution status
if (event.outcome === 'exception') {
// Script threw uncaught exception
}
// ✅ Check HTTP status separately
if (event.event?.response?.status === 500) {
// HTTP 500 returned (script may have handled error)
}
```
## Serialization Considerations
`log.message` is `unknown[]` and may contain non-serializable objects:
```typescript
// ❌ May fail with circular references or BigInt
JSON.stringify(events)
// ✅ Safe serialization
const safePayload = events.map((event) => ({
...event,
logs: event.logs.map((log) => ({
...log,
message: log.message.map((m) => {
try {
return JSON.parse(JSON.stringify(m))
} catch {
return String(m)
}
}),
})),
}))
```
**Common serialization issues:**
- Circular references in logged objects
- `BigInt` values (not JSON-serializable)
- Functions or symbols in console.log arguments
- Large objects exceeding body size limits
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.