`authdog` 0.1.0 is the official Zig client for the [Authdog REST API](/docs/api). Add it from the [authdog/sdk](https://github.com/authdog/sdk) monorepo. It is a management and userinfo client, not a web-framework session binding.

Need to protect a web route? Use a [backend SDK](/docs/backend) for your server framework, or call `userinfo` yourself with this client.

## Install

Add the dependency to `build.zig.zon`:

```zig
.dependencies = .{
    .authdog = .{
        .url = "https://github.com/authdog/sdk/archive/refs/heads/main.tar.gz",
        .hash = "YOUR_PACKAGE_HASH",
    },
},
```

Then in `build.zig`:

```zig
const authdog_dep = b.dependency("authdog", .{
    .target = target,
    .optimize = optimize,
});
exe.root_module.addImport("authdog", authdog_dep.module("authdog"));
```

Pin a commit (or a tagged archive) and put the matching package hash in `build.zig.zon`. Requires Zig 0.16.0+. The SDK uses only the Zig standard library. Source: [`zig/`](https://github.com/authdog/sdk/tree/main/zig) in [authdog/sdk](https://github.com/authdog/sdk).

## Configure

Construct one client with the public API base URL. Pass a management Bearer credential (`ad_…`) when you call privileged endpoints:

```zig
const std = @import("std");
const authdog = @import("authdog");

var client = try authdog.AuthdogClient.init(allocator, .{
    .base_url = "https://api.authdog.com",
    .api_key = std.posix.getenv("AUTHDOG_API_TOKEN"),
    .timeout_ms = 10_000,
});
defer client.deinit();
```

Keep the token server-side. `getUserInfo` still uses the caller access token, not the management key.

`health()` is public and works without an API key:

```zig
const probe = try client.health();
```

Optional constructor fields `environment_secret` (`adenv_`), `scim_token` (`adscim_`), and `hris_token` (`adhris_`) are the AuthZEN/MCP runtime, SCIM, and HRIS Bearers.

## Resolve a user from an access token

```zig
const user_info = client.getUserInfo(access_token) catch |err| {
    if (authdog.isAuthenticationError(err)) {
        // 401: missing, invalid, or expired access token
        return err;
    }
    if (authdog.isApiError(err)) {
        // transport or non-401 HTTP failure
        return err;
    }
    return err;
};
defer user_info.deinit();

_ = user_info.user.display_name;
if (user_info.user.emails.len > 0) {
    _ = user_info.user.emails[0].value;
}
```

`GET /v1/userinfo` always sends `Authorization: Bearer <access-token>`. A constructor API key does not replace that header.

`UserInfoResponse` uses snake_case fields (`user.display_name`, `session.remaining_seconds`).

## Call the management API

Namespaces wrap Waves 1–3 of the public `/v1` surface:

| Attribute | Resources |
| --- | --- |
| `organizations` | Organizations, invitations, members, keys |
| `tenants` | Tenants, domains, seats |
| `projects` | Applications under a tenant |
| `environments` | Environment records |
| `users` | Directory users in a tenant + environment |
| `groups` | Groups and membership |
| `rbac` | Roles, permissions, resources, mappings, ABAC |
| `audit` | Administrative audit logs |
| `events` | Identity event stream |
| `webhooks` | Webhook subscriptions |
| `notificationChannels` | SIEM / notification channels |
| `serviceAccounts` | Service accounts |
| `personalAccessTokens` | PATs |
| `apiSecrets` | Environment API secrets |
| `authzen` | AuthZEN evaluate, search, and discovery |
| `scim` | SCIM 2.0 directory |
| `hris` | HRIS employees and departments |
| `mcp` | MCP runtime |
| `otel` | OpenTelemetry exporters |
| `oidcClients` | OIDC clients |
| `actions` | Environment actions |
| `addons` | Add-ons |
| `billing` | Billing |
| `settings` | Environment settings |
| `elevate` | Elevate |
| `emailProviders` | Email providers |
| `featureFlags` | Feature flags |
| `forms` | Forms |
| `provisioningTokens` | Provisioning tokens |
| `impersonation` | Impersonation |
| `portal` | Account portal |
| `security` | Security settings |
| `threats` | Threats |
| `vanityDomains` | Vanity domains |
| `widgets` | Widgets |

```zig
const orgs = try client.organizations.list();
defer orgs.deinit();

const users = try client.users.list("ten_123", "env_456", .{ .limit = 25 });
defer users.deinit();
```

Directory calls take `tenant_id` then `environment_id`. OpenAPI at [`/v1/openapi`](https://api.authdog.com/v1/openapi) is the field-level contract.

## Errors

| Error | When |
| --- | --- |
| `AuthenticationFailed` | HTTP 401 |
| `ApiError` | Other HTTP failures and transport errors |
| `ParseError` | HTTP 200 with a body that is not valid user-info JSON |

Use `isAuthenticationError` / `isApiError` to branch on type. Use `client.lastErrorMessage()` for the stable message text.

## Next

- [API reference](/docs/api): auth, versioning, and resource families
- [Backend requests](/docs/backend): validate sessions on incoming requests
- [Python SDK](/docs/sdks/python): the same client surface in Python
- [Users](/docs/users): directory model the `users` namespace talks to
