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

# Go SDK — AuditRails Context-Aware Audit Log Client

> Integrate AuditRails into any Go application. Zero dependencies, context-aware logging, automatic batching, and structured logging via slog.

The AuditRails Go SDK is a zero-dependency client built entirely on the standard library (`net/http`). It is designed to be idiomatic Go: context-aware, safe for concurrent use from multiple goroutines, and properly closeable via `defer client.Close()`. Events are buffered and flushed in a background goroutine so your hot paths are never blocked by network I/O.

## Installation

```bash theme={null}
go get github.com/auditrails/auditrails-go
```

Requires **Go 1.21 or later**.

## Initialization

Create a client with `auditrails.New()`. The first argument is your API key; the second is an optional `*Options` struct. Always pair client creation with `defer client.Close()` to ensure buffered events are flushed when your application exits.

```go theme={null}
package main

import (
    "os"

    auditrails "github.com/auditrails/auditrails-go"
)

func main() {
    client, err := auditrails.New(os.Getenv("AUDITRAILS_API_KEY"), nil)
    if err != nil {
        panic(err)
    }
    defer client.Close()

    // Your application logic here
}
```

Pass `nil` as the second argument to use all defaults, or supply an `*Options` struct to customise behaviour:

```go theme={null}
client, err := auditrails.New(os.Getenv("AUDITRAILS_API_KEY"), &auditrails.Options{
    BatchSize:     50,
    FlushInterval: 500 * time.Millisecond,
    MaxRetries:    5,
    Logger:        slog.Default(),
})
```

### Configuration options

| Field           | Type            | Default                     | Description                                                   |
| --------------- | --------------- | --------------------------- | ------------------------------------------------------------- |
| `BaseURL`       | `string`        | `https://api.auditrails.io` | Override the API endpoint.                                    |
| `BatchSize`     | `int`           | `100`                       | Maximum events per HTTP request.                              |
| `FlushInterval` | `time.Duration` | `1s`                        | How often the buffer is flushed.                              |
| `MaxRetries`    | `int`           | `3`                         | Retry attempts on `5xx` responses.                            |
| `Timeout`       | `time.Duration` | `10s`                       | Per-request HTTP timeout.                                     |
| `Logger`        | `*slog.Logger`  | `nil` (silent)              | Structured logger for internal diagnostics.                   |
| `HTTPClient`    | `*http.Client`  | *(default)*                 | Provide a custom `http.Client` (e.g. with a transport proxy). |

## Logging events

### Buffered logging (recommended)

`client.Log()` appends an `Event` to the in-memory buffer and returns immediately. The buffer is flushed in the background on `FlushInterval`. This method **never returns an error** — failures are logged internally if a `Logger` is configured.

```go theme={null}
client.Log(auditrails.Event{
    Action:   "user.login",
    ActorID:  "user_123",
    Resource: "session/sess_abc",
    Metadata: map[string]any{
        "ip":     "203.0.113.1",
        "method": "oauth2",
    },
})
```

### Context-aware buffered logging

`client.LogWithContext()` behaves identically to `Log()` but respects context cancellation. If the context is already done when the call is made, the event is silently dropped. Use this in request handlers where you want cancellation to propagate.

```go theme={null}
func viewDocumentHandler(w http.ResponseWriter, r *http.Request) {
    docID := chi.URLParam(r, "id")
    userID := r.Header.Get("X-User-ID")

    doc, err := fetchDocument(r.Context(), docID)
    if err != nil {
        http.Error(w, "not found", http.StatusNotFound)
        return
    }

    client.LogWithContext(r.Context(), auditrails.Event{
        Action:   "document.viewed",
        ActorID:  userID,
        Resource: fmt.Sprintf("document/%s", docID),
    })

    json.NewEncoder(w).Encode(doc)
}
```

### Direct (immediate) logging

`client.LogDirect()` sends the event immediately and returns the API response. It **returns an error** on failure.

```go theme={null}
resp, err := client.LogDirect(ctx, auditrails.Event{
    Action:  "document.deleted",
    ActorID: "user_456",
    Resource: "document/doc-789",
})
if err != nil {
    log.Printf("audit log failed: %v", err)
    return
}
fmt.Println(resp.LogID) // "01HXYZ..."
```

### Direct batch logging

Send multiple events in one HTTP request, bypassing the buffer.

```go theme={null}
_, err := client.LogBatchDirect(ctx, []auditrails.Event{
    {Action: "document.created", ActorID: "user_123", Resource: "document/doc-001"},
    {Action: "document.shared",  ActorID: "user_123", Resource: "document/doc-001"},
})
```

### Manual flush

```go theme={null}
client.Flush()
```

## Framework integration

### Standard `net/http` middleware

Wrap any `http.Handler` with a middleware function to automatically audit every request that passes through it.

```go theme={null}
package main

import (
    "net/http"

    auditrails "github.com/auditrails/auditrails-go"
)

func auditMiddleware(client *auditrails.Client, action string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            next.ServeHTTP(w, r)
            client.LogWithContext(r.Context(), auditrails.Event{
                Action:   action,
                ActorID:  r.Header.Get("X-User-ID"),
                Resource: r.Method + " " + r.URL.Path,
            })
        })
    }
}

func main() {
    client, _ := auditrails.New(os.Getenv("AUDITRAILS_API_KEY"), nil)
    defer client.Close()

    mux := http.NewServeMux()
    mux.Handle("DELETE /documents/{id}", auditMiddleware(client, "document.deleted")(deleteHandler))

    http.ListenAndServe(":8080", mux)
}
```

This pattern composes cleanly with any router that follows the standard `http.Handler` interface (Chi, Gorilla Mux, etc.).

## Error handling

`Log()` and `LogWithContext()` never return errors. For `LogDirect()` and `LogBatchDirect()`, use `errors.As` to unwrap an `*auditrails.APIError` and inspect the structured fields.

```go theme={null}
import "errors"

resp, err := client.LogDirect(ctx, auditrails.Event{
    Action:  "user.login",
    ActorID: "user_123",
})
if err != nil {
    var apiErr *auditrails.APIError
    if errors.As(err, &apiErr) {
        fmt.Println(apiErr.StatusCode)  // e.g. 401
        fmt.Println(apiErr.Code)        // e.g. "INVALID_API_KEY"
        fmt.Println(apiErr.RequestID)   // AuditRails request ID for support
        fmt.Println(apiErr.DocURL)      // Link to relevant documentation
        fmt.Println(apiErr.Retryable()) // true/false
    } else {
        // Network-level error (timeout, DNS failure, etc.)
        fmt.Println("network error:", err)
    }
}
```

## Graceful shutdown

Calling `client.Close()` flushes all buffered events and stops the background goroutine. Always use `defer client.Close()` immediately after creating a client so shutdown is guaranteed even if your `main` function returns early.

```go theme={null}
client, err := auditrails.New(os.Getenv("AUDITRAILS_API_KEY"), nil)
if err != nil {
    log.Fatal(err)
}
defer client.Close() // Always paired with New()
```

For long-running services that handle OS signals, you can wire `client.Close()` into your signal handler as well:

```go theme={null}
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit

client.Close() // Flush before exiting
```

<Note>
  `client.Close()` is safe to call multiple times. Subsequent calls after the first are no-ops.
</Note>
