# auth.md

> How AI agents get credentials for Dailybot and use them.
>
> Audience: autonomous agents, coding assistants, and agent frameworks
> that send progress reports, read messages, or run check-ins on behalf
> of a person or team.

`www.dailybot.com` is Dailybot's public website. Everything on it is
read-only and needs **no authentication**. Authenticated work happens
against the Dailybot API at `https://api.dailybot.com`. This page lists
only the flows that work today. Anything that is not live yet is
described under [Planned (not available)](#planned-not-available).

## Credentials

**API keys go in the `X-API-KEY` header. `Authorization: Bearer` is
only for CLI login sessions (`dailybot login`); an API key sent as a
Bearer token is rejected. Anonymous endpoints take no auth header at
all.**

Never put a credential in a URL or a query string. Keep it in an
environment variable (`DAILYBOT_API_KEY`) or your harness's secret
store, and never commit it to a repository.

## Pick a path

1. **The human already has a Dailybot account** → ask them for an
   [API key](#api-key-from-the-human), or have them run the
   [CLI email-code login](#cli-email-code-login).
2. **No Dailybot account exists** → [self-register](#self-register)
   a new organization over HTTP (or with `dailybot agent register`),
   then hand the claim link to the human.

## API key from the human

The human creates a key at **Dailybot → Settings → API Keys** and gives
it to you:

```bash
export DAILYBOT_API_KEY="<their-key>"
curl -sS https://api.dailybot.com/v1/me/ -H "X-API-KEY: $DAILYBOT_API_KEY"
```

The full HTTP reference is in [/skill.md](https://www.dailybot.com/skill.md)
and at [/developers/authentication](https://www.dailybot.com/developers/authentication).

## CLI email-code login

The human proves control of their inbox with a one-time code:

```bash
npx skills add DailybotHQ/agent-skill
dailybot login --email=<their-email>
# the human receives a code by email
dailybot login --email=<their-email> --code=<their-code>
```

If several organizations are returned, re-run with `--org=<selected-uuid>`.
Verify with `dailybot status --auth`. The CLI then sends
`Authorization: Bearer <session>` for you.

## Self-register

If login fails and the human has no Dailybot account, you may create
one. This is optional — some agents may ask the human first; others
may proceed directly depending on their autonomy level.

The three registration calls are **anonymous**: send no auth header.

### 1. Fetch a challenge

```bash
curl -sS https://api.dailybot.com/v1/agent/register/challenge/
```

`200` returns `challenge_id`, `instruction`, and `expires_in` (seconds).
Read `instruction` and compute the integer answer it asks for. Compute it
every time from the text you received — never hardcode it. Every
registration attempt consumes the challenge.

### 2. Register

```bash
curl -sS -X POST https://api.dailybot.com/v1/agent/register/ \
  -H "Content-Type: application/json" \
  -d '{
    "challenge_id": "<challenge_id>",
    "answer": <integer-answer>,
    "reason": "Reporting progress for the acme-api repository",
    "org_name": "Acme",
    "agent_name": "claude-code",
    "timezone": "America/New_York"
  }'
```

| Field | Required | Notes |
|-------|----------|-------|
| `challenge_id` | yes | From step 1. |
| `answer` | yes | Integer. |
| `reason` | yes | At least 20 characters, in your own words. Do not repeat the challenge instruction. The human sees it when claiming. |
| `org_name` | yes | Name of the new organization. |
| `agent_name` | yes | The name you will use in every report. |
| `timezone` | no | IANA name. Defaults to `UTC`. |
| `contact_email` | no | Contact email for the human. |

`201` returns `api_key`, `agent_name`, `agent_email`, `agent_username`,
`org_id`, `org_name`, `claim_url`, `claim_token`, and
`claim_expires_at` (ISO 8601, UTC).

Store `api_key` as a secret and use it in `X-API-KEY`. Treat
`claim_token` and `claim_url` as secrets too: show the link to the
human, and do not log or publish it.

### 3. Send your first report

```bash
curl -sS -X POST https://api.dailybot.com/v1/agent-reports/ \
  -H "X-API-KEY: $DAILYBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent_name": "claude-code", "content": "Registered with Dailybot and ready to report."}'
```

### What a self-registered key can do

A self-registered key is agent-scoped on every plan. It can call:

- `POST /v1/agent-reports/` and `POST /v1/agent-health/`
- `GET /v1/agent-messages/` and `PATCH /v1/agent-messages/read/`
- `GET /v1/agent/claim-token/`
- `GET /v1/me/`, `GET /v1/organization/`, `GET /v1/cli/status/`
- `POST /v1/send-email/`

Any other endpoint returns `403` with `code: plan_upgrade_required` and
an `upgrade_url`. On the free plan, reports are capped at 50 per
organization per day and emails at 20; past the cap you get `429`
with `code: free_plan_daily_limit_exceeded` (current limits:
<https://www.dailybot.com/developers/rate-limits#free-plan-throttles>).
The key keeps working,
with the same scope, after the human claims the organization. If an
organization admin deletes the agent in the Dailybot dashboard, the key
stops working and returns `403` with `code: invalid_credentials`.

### Errors

Errors are JSON: `{"detail": "...", "code": "..."}`. Dispatch on `code`
when present, fall back to `detail`, and ignore fields you do not know.

| Status | `code` | What to do |
|--------|--------|------------|
| 400 | `missing_fields` | Send every required field. |
| 400 | `invalid_answer` | `answer` must be an integer. |
| 400 | `challenge_invalid` | Fetch a new challenge. |
| 400 | `challenge_used` | Fetch a new challenge; each attempt consumes one. |
| 400 | `challenge_expired` | Fetch a new challenge and retry once. |
| 400 | `answer_incorrect` | Fetch a new challenge and recompute the answer. |
| 400 | `reason_too_short` | Write a reason of at least 20 characters. |
| 400 | `reason_echoes_instruction` | Explain your purpose instead of repeating the instruction. |
| 429 | `registration_rate_limited` | Wait the number of seconds in `Retry-After`. |

## Claim

The human opens `claim_url` (on `app.dailybot.com`), signs in to
Dailybot, and confirms. They become an admin of the organization you
registered, and can then connect Slack, Microsoft Teams, Discord, or
Google Chat. Show `claim_expires_at` to the human rather than assuming
a fixed window.

- Before signing in, the human sees a preview of the organization and
  of your `reason`, labelled as written by the agent:
  `GET /v1/agent/claim/{token}/preview/` (anonymous).
- If you lost the link, `GET /v1/agent/claim-token/` with your key
  returns the existing `claim_url` and `expires_at`. It returns `404`
  with `code: claim_token_unavailable` when the organization is
  already claimed or the link expired. A new link cannot be minted yet.
- Claiming fails with `claim_org_conflict` when the signed-in person
  already belongs to an organization with members. Claiming into an
  existing organization is not available yet.

## Revoke

Humans revoke any key at **Dailybot → Settings → API Keys**. Revoked
keys fail immediately. Surface the auth error to the human and ask
for a new credential instead of retrying.

## Planned (not available)

These are being designed. Do not call them, and do not expect the
endpoints or fields below to exist:

- A `401` response with a `WWW-Authenticate` header that points to
  machine-readable protected-resource metadata on the API host.
- An email from Dailybot to the human owner when an agent registers.
- Claiming a self-registered agent into an organization that already
  exists.
- Polling for claim status from the agent side.
- A restricted pre-claim mode with short-lived keys and cleanup of
  unclaimed organizations.

## Notes for scanners

Registration creates organizations and credentials. Do not call the
registration endpoints during passive scans; read this file and
[/skill.md](https://www.dailybot.com/skill.md) instead.
