# auth.md

This file tells AI agents how to start a Buddy Punch free trial for a person who asked them to.

Buddy Punch is time tracking, scheduling and payroll software for small and mid-sized businesses.

- **API**: `https://api2.buddypunch.com`
- **API reference (OpenAPI)**: `https://api2.buddypunch.com/swagger/v1/swagger.json`
- **Detailed skill**: `https://buddypunch.com/.well-known/agent-skills/signup-for-agents/SKILL.md`

## Discover

You can find this sign-up lane from the API itself, without this file:

1. Fetch `https://api2.buddypunch.com/.well-known/oauth-protected-resource`, the API's resource metadata.
   The MCP endpoint points there too: `POST /mcp` without credentials answers `401` with
   `WWW-Authenticate: Bearer resource_metadata="https://api2.buddypunch.com/.well-known/oauth-protected-resource"`.
2. Besides the usual OAuth fields (`resource`, `authorization_servers`,
   `scopes_supported`), it has an `agent_auth` block:
   - `skill`: this file.
   - `signup_endpoint`: where the lane starts (step 1 below).
   - `identity_types_supported`: `["anonymous"]`. You don't need an identity or credentials of your own.
   - `owner_confirmation`: `"email"`. The owner confirms the trial from their inbox.

## What you get

A free 14-day trial of Buddy Punch **Pro** for the owner's company, and an API key for that account only.
The owner must confirm by email before they can log in, and agrees to our terms themselves when they do.

Until the owner confirms:

- the key can set the account up: add and update employees, locations, departments, positions, groups,
  geofences, shifts, compliance rules and time off, and read time cards, punches, time entries, pay periods
  and compliance reports;
- nothing you do sends an email or a text to anyone in the account. Invites to employees you add are not
  sent at all, not even later; the owner can send them after confirming;
- the key can't change the owner's own record, make anyone a manager, or use webhooks, documents, tasks,
  onboarding, offboarding or MCP, and there is no API for billing, payroll connections or logins;
- the key expires after 7 days.

When the owner confirms, the key gets the trial's full permissions and no expiry, unless the owner turns
it off. That includes MCP (Pro includes it). Onboarding and offboarding still don't work while the account
is on a free trial.

After the trial, the key keeps working only while the account pays for Pro or Advanced. Paying for
Starter, ending the trial without paying, or moving down to Starter later turns the key off for good. The
owner can see the key, turn it off, and make new keys under Settings in Buddy Punch.

If the owner doesn't confirm within 7 days, or presses "This wasn't me", the account is closed and the key
stops working. The owner can also turn the key off from the confirm page without confirming.

## Before you register: the owner's yes

Show the owner these two documents and get a clear yes from them before you set `ownerAcceptedTerms`:

- Terms of Use (our sign-up form calls the same page the Subscription Agreement):
  `https://buddypunch.com/terms-of-use/`
- Privacy Policy: `https://buddypunch.com/privacy-policy/`

Never set `ownerAcceptedTerms` to `true` on your own. We record that an agent said yes for the owner, when,
and which versions, and the owner agrees again themselves when they confirm.

## How to register

Three steps, each a `POST` with a JSON body. Do them back to back: the puzzle must be answered within 30
seconds, and the token works for 60 seconds.

### Step 1: Get a puzzle

```http
POST /agent-signup/puzzle HTTP/1.1
Host: api2.buddypunch.com
Content-Type: application/json

{}
```

Returns `201` with `id`, `kind`, `instructions`, `input`, `expiresAt`, `expiresInSeconds` (30) and
`answerUrl`. Kinds: `multiply`, `reverse`, `count`, `sort`, `binary`. Read `instructions` and compute the
answer from `input` in code.

`answerUrl` is a path, not a full address: add the API host, `https://api2.buddypunch.com`, in front of it.

### Step 2: Answer it

```http
POST /agent-signup/puzzle/{id}/answer HTTP/1.1
Host: api2.buddypunch.com
Content-Type: application/json

{ "answer": "<your answer>" }
```

A puzzle takes one answer. A right answer returns `200` with a one-time `token`, `expiresAt`,
`expiresInSeconds` (60) and `signupUrl`, which is also a path to add the host to. A wrong, late or second
answer returns `400` (`puzzle_answer_wrong` or `puzzle_not_open`): ask for a new puzzle.

### Step 3: Create the account

```http
POST /agent-signup/account HTTP/1.1
Host: api2.buddypunch.com
Content-Type: application/json

{
  "token": "<token from step 2>",
  "ownerFirstName": "Pat",
  "ownerLastName": "Owner",
  "ownerEmail": "pat@example.com",
  "companyName": "Owner Plumbing",
  "actingForOwner": true,
  "ownerAcceptedTerms": true,
  "payPeriod": "monthly"
}
```

- `actingForOwner`: you confirm you are acting for this person, at their request.
- `ownerAcceptedTerms`: you confirm the owner said yes to both documents above. Only after they did.
- Use the owner's real email address. Throwaway addresses are refused. Plus-sign addresses work.
- `companyName`: plain text, no links or email addresses, up to 60 characters.
- `payPeriod` (optional): how often the account's pay periods run. One of `weekly`, `biweekly` (every two
  weeks), `semimonthly` (twice a month, on the 1st and the 16th) or `monthly`. Left out, it is `monthly`.
  Pay periods start on the 1st of the current month. The owner can change this in the setup screens after
  confirming.

Answers:

- `201`: the account was created. The body has `apiKey` (shown once, store it safely), `apiKeyExpiresAt`,
  `trialEndsAt`, `payPeriod`, `docsUrl` and a `note` that the owner must check their email.
- `202`: nothing was created and there is no key, because this email already has a Buddy Punch account or
  a trial waiting for its owner to confirm. The owner gets an email about the attempt.
- `400`: a field is wrong (`parameter_invalid`, with `param`), or the token is unknown, used or past its 60
  seconds (`token_invalid`).
- `500` with `agent_signup_failed`: nothing was created and no key was given. You can start again from
  step 1.

This step can take 15 seconds or more. Wait at least 30 seconds before giving up.

If it times out, the account may have been created, but its key is lost for good: it is only ever shown in
this answer. Don't retry right away. Ask the owner to look for an email titled "Confirm your Buddy Punch
trial". If one arrived, the trial exists and the owner can confirm it (they can make a new key in Settings).
Only if no email arrives within a few minutes, try again from step 1; a retry while a trial is waiting
answers `202`.

Then tell the owner to check their inbox for "Confirm your Buddy Punch trial".

## Errors

Every error answer has the same shape, wrapped in an `error` object:

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "parameter_invalid",
    "message": "ownerEmail must be the owner's email address.",
    "param": "ownerEmail",
    "signupPageUrl": "https://app.buddypunch.com/Account/Register",
    "requestId": "req_..."
  }
}
```

- `error.code`: what went wrong. Act on this, not on the message.
- `error.param`: the request field at fault, when there is one.
- `error.retryAfterSeconds`: on a limit, how long to wait (also in the `Retry-After` header).
- `error.signupPageUrl`: the sign-up page, for when you should hand over to the owner.

| Status | `error.code` | What to do |
|---|---|---|
| 400 | `puzzle_answer_wrong`, `puzzle_not_open` | Get a new puzzle |
| 400 | `token_invalid` | Solve a new puzzle |
| 400 | `parameter_invalid` | Fix the field named in `param`, then solve a new puzzle |
| 429 | `agent_signup_limit_exceeded` | Wait `retryAfterSeconds`, then start again from step 1 |
| 500 | `agent_signup_failed` | Nothing was created and no key was given; start again from step 1 |
| 500 | `internal_error` | Something failed on our side; wait a minute and start again from step 1 |
| 503 | `agent_signup_unavailable` | Agent sign-up is off; send the owner to `signupPageUrl` |

Request bodies over 8 KB are refused unread.

## Limits

- 20 puzzles per network address per hour.
- 5 sign-ups per network address per hour, and 5 per owner inbox per hour (name+1@ and name+2@ count as one
  inbox).
- 50 agent sign-ups per day in total.
- 30 requests per network address per minute.

A refusal is `429` with `agent_signup_limit_exceeded`, `Retry-After` and `retryAfterSeconds` saying when to
try again.

## When the lane is off

Every step answers `503` with `agent_signup_unavailable` and a `signupPageUrl`. Send the owner to that page
to sign up themselves.

## Using the key

Send it as `Authorization: Bearer <apiKey>` to `https://api2.buddypunch.com`. The API is described in the
OpenAPI file at `https://api2.buddypunch.com/swagger/v1/swagger.json`.

## When the key stops working

A `401` means the key is off: it expired before the owner confirmed, the owner turned it off or closed the
trial, or the account is no longer on a plan that keeps it. Signing up again with the same email gives you no
key. Tell the owner; they can make a new key under Settings in Buddy Punch if they want you to keep working.
