# auth.md — AI Content Drop

You are probably an AI agent reading this because you want to use AI Content Drop
on a person's behalf. This file tells you what is actually possible today, so you
don't waste turns guessing at forms.

AI Content Drop is an AI video, image, and ad generation platform at
<https://aicontentdrop.com>. Users describe what they want, the platform routes to
the best model, generates the content, and bills credits.

Protocol: [auth.md](https://auth-md.com/) — agent-readable registration instructions.

**Short version:** reading is free and needs no credential. Generating needs an API
key (`acd_live_…`) that a signed-in human creates for you in about ten seconds.
There is no programmatic account signup.

## Discover

Everything an agent needs to find is published at a fixed URL:

| What | Where |
|---|---|
| This document | <https://aicontentdrop.com/auth.md> |
| Protected-resource metadata (RFC 9728) | <https://aicontentdrop.com/.well-known/oauth-protected-resource> |
| Authorization-server metadata (RFC 8414 / OIDC) | <https://aicontentdrop.com/.well-known/oauth-authorization-server> |
| OpenID Connect discovery | <https://aicontentdrop.com/.well-known/openid-configuration> |
| API catalogue (RFC 9727) | <https://aicontentdrop.com/.well-known/api-catalog> |
| REST API spec (OpenAPI 3.1) | <https://aicontentdrop.com/openapi.json> |
| Human documentation | <https://aicontentdrop.com/developers> |
| MCP manifest | <https://aicontentdrop.com/.well-known/mcp.json> |
| A2A agent card | <https://aicontentdrop.com/.well-known/agent-card.json> |

The protected-resource metadata names the `resource`, its `authorization_servers`,
and `bearer_methods_supported: ["header"]`. The authorization-server documents are
live mirrors of our identity provider's own metadata, so the issuer named there
always matches the one in the protected-resource document.

The issuer they name is `https://auth.aicontentdrop.com`. That host exists **only**
as the OAuth/OIDC issuer — its endpoints are the ones the metadata documents list,
and its root path serves a JSON 404 by design (it is the identity provider's API
surface, not a website). Do not probe it looking for a signup page: account
creation starts at `POST https://aicontentdrop.com/v1/auth/register` for agents,
or <https://aicontentdrop.com/register> for a human in a browser.

You do not have to fetch any of that first. **Every 401 tells you where to look**,
in a `WWW-Authenticate` header carrying `resource_metadata`:

```http
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="AI Content Drop",
  resource_metadata="https://aicontentdrop.com/.well-known/oauth-protected-resource",
  error="invalid_token", error_description="Invalid or revoked API key"
```

Follow `resource_metadata` to the `oauth-protected-resource` document, then its
`authorization_servers[0]` to the `oauth-authorization-server` document. That
second document carries the `agent_auth` block described under **Register**.

## Pick a method

Choose by what you are trying to do. Only one of these lets you spend money, and it
always belongs to a human who chose to give it to you.

| Your goal | Method | Credential needed |
|---|---|---|
| Read pages, articles, prices, model costs | Anonymous HTTP, MCP, A2A, or NLWeb | **None** |
| Ask a natural-language question about models or guides | `POST /ask` | **None** |
| List models and quote credit costs | `GET /v1/models`, `GET /v1/models/{id}/cost` | **None** |
| Generate video or images, read an account | REST `/v1` or the MCP generation tools | **API key** (`acd_live_…`) |
| Create an account, buy credits, change a subscription | Hand off to a human | Not available to agents |

**Do not** attempt these — they are not oversights, they are the boundary:

- POSTing to any `/api/*` path to create an account. `/api/` is `Disallow`ed in
  [robots.txt](https://aicontentdrop.com/robots.txt); those are private application
  internals that change without notice. The public API is `/v1`.
- Scripting the signup form or the hosted checkout. Both are human flows, and
  automated attempts are treated as abuse.
- Reusing or sharing credentials between users. One account, one human owner.

## Register

There are two ways to register, and the difference between them is what they can
spend. Self-registration is open to you right now and buys you rate limit, not
money. Anything that spends credits belongs to a human who chose to hand it over.

### `agent_auth` — self-registration, no human, live today

The authorization-server metadata carries an `agent_auth` block. Fetch it and read
the URIs from there rather than hard-coding these paths:

```bash
curl -s https://aicontentdrop.com/.well-known/oauth-authorization-server \
  | jq .agent_auth
```

```json
{
  "version": "1",
  "skill": "https://aicontentdrop.com/auth.md",
  "register_uri": "https://aicontentdrop.com/agent/auth/register",
  "claim_uri": "https://aicontentdrop.com/agent/auth/claim",
  "revocation_uri": "https://aicontentdrop.com/agent/auth/revoke",
  "identity_types_supported": ["anonymous"],
  "anonymous": {
    "credential_types_supported": ["bearer"],
    "scopes_supported": ["read"]
  }
}
```

**POST to `register_uri`** and you get a usable credential in the same response —
no second round trip, no approval queue:

```bash
curl -sX POST https://aicontentdrop.com/agent/auth/register \
  -H "Content-Type: application/json" \
  -d '{ "client_name": "my-research-agent", "identity_type": "anonymous" }'
```

```json
{
  "client_id": "acd_agent_client_…",
  "client_secret": "…",
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "read",
  "claim_uri": "https://aicontentdrop.com/agent/auth/claim",
  "revocation_uri": "https://aicontentdrop.com/agent/auth/revoke"
}
```

Keep `client_id` and `client_secret`. **POST them to `claim_uri`** to mint a fresh
token when the first expires, and **POST to `revocation_uri`** to destroy one you
no longer trust. See **Claim**, **Use the credential**, and **Revocation** below.

What that token is and is not:

- It is `scope: read`. It raises your rate limit on the public read surface.
- It cannot generate, spend credits, read an account, or write anything.
- Reading works **without** it. Register only if you are being throttled.

**`identity_assertion` is not supported, and that is deliberate.**
`identity_types_supported` lists `anonymous` only. We accept no identity assertion
from a calling platform and issue no identity-assertion JWT authorization grant
(**`id-jag`**) of our own, so there is no way for an agent to arrive already
claiming to act for a named user. A credential that spends money is created
deliberately, by that human, in our UI. If you send `identity_type` anything other
than `"anonymous"`, `register_uri` answers `400 unsupported_identity_type` and
names what it does support rather than failing vaguely.

### The full registration matrix

| Method | Status | Endpoint |
|---|---|---|
| Agent self-registration (`agent_auth`, anonymous, read scope) | **supported** | `register_uri` → <https://aicontentdrop.com/agent/auth/register> |
| API key issued by a signed-in human (spends credits) | **supported** | <https://aicontentdrop.com/settings/integrations> |
| Human web signup (email + password, Google OAuth, magic link) | supported | <https://aicontentdrop.com/register> |
| `identity_assertion` / `id-jag` cross-domain agent identity | **not supported** | — see above |
| OAuth 2.0 authorization code (user-consented) | discovery published, no `registration_endpoint` yet | AS metadata |
| Dynamic client registration (RFC 7591) | not enabled | would appear as `registration_endpoint` in AS metadata |
| Anonymous read access to all public content | supported, no credentials | see **Discover** |

Our authorization-server metadata stays the source of truth: if it ever grows a
`registration_endpoint`, dynamic client registration (RFC 7591) and the
authorization-code flow are live and you may register a client and request
user-consented tokens. Until then, do not improvise against the token endpoint.

## Claim

### Claiming an `agent_auth` token yourself

The token from `register_uri` expires. Exchange the `client_id` and
`client_secret` you kept for a fresh one at `claim_uri` — no human involved:

```bash
curl -sX POST https://aicontentdrop.com/agent/auth/claim \
  -H "Content-Type: application/json" \
  -d '{ "client_id": "acd_agent_client_…", "client_secret": "…" }'
```

The response has the same shape as registration: a bearer `access_token`, its
`expires_in`, and `scope: read`. Claim on expiry, not on a timer.

### Claiming a credential that can spend

For anything that costs credits, ask your human to do this — it takes about ten
seconds and needs no sales call:

1. Sign in, or sign up free (10 credits, no card) at
   <https://aicontentdrop.com/register>.
2. Open <https://aicontentdrop.com/settings/integrations>.
3. Create an API key. Optionally set a **daily credit ceiling** on it, which caps
   what you can spend per UTC day no matter what you do.
4. Copy the key. It is shown exactly once and starts with `acd_live_`.

You cannot perform any of those steps yourself, and you should not try. If a page
asks you for an account password, something is wrong — no flow here needs one.

## Use the credential

Send the key as a bearer token on every request:

```
Authorization: Bearer acd_live_YOUR_KEY
```

Verify it works, then generate:

```bash
# 1. Who am I? (confirms the key and shows the credit balance)
curl https://aicontentdrop.com/v1/me \
  -H "Authorization: Bearer acd_live_YOUR_KEY"

# 2. Start a generation (returns immediately, status "generating")
curl -X POST https://aicontentdrop.com/v1/generate/video \
  -H "Authorization: Bearer acd_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d "{ \"prompt\": \"a golden retriever surfing at sunset\", \"aiModel\": \"kling_3_0\" }"

# 3. Poll until status is "completed"
curl https://aicontentdrop.com/v1/videos/VIDEO_ID \
  -H "Authorization: Bearer acd_live_YOUR_KEY"
```

An `agent_auth` token goes in the same header and is read the same way:

```
Authorization: Bearer <access_token from register_uri or claim_uri>
```

The server tells the two apart by prefix. A key starting `acd_live_` can generate;
an `agent_auth` token raises your read rate limit and nothing else. Sending one
where the other is required returns `401` with a `WWW-Authenticate` header naming
the metadata document, never a redirect to a login page.

The same key works on the MCP server (`POST /mcp`). All eight tools are listed
anonymously so you can see the shape of the surface before committing to a
credential; the four generation and account tools say `REQUIRES AN API KEY` in
their description and refuse `tools/call` without one.

Costs are flat per model — quote before you commit with
`GET /v1/models/{id}/cost?quantity=N`. Credits are charged only when a generation
succeeds: safety blocks, provider failures, and timeouts cost the user nothing.

**Rate limits:** 120 requests/minute. Every response carries `RateLimit-Limit`,
`RateLimit-Remaining`, and `RateLimit-Reset`. Honour them rather than discovering
them by getting throttled.

## Errors

Every failure is structured JSON — never an HTML page:

```json
{ "error": { "code": "unauthorized", "message": "Provide an API key as Authorization: Bearer acd_live_…" } }
```

| Status | `code` | What to do |
|---|---|---|
| 400 | `invalid_request` | Fix the named field. Do not retry unchanged. |
| 401 | `unauthorized` | The key is missing, malformed, revoked, or over its daily ceiling. Read the `WWW-Authenticate` header: it carries `resource_metadata` pointing at the protected-resource document, and `error="invalid_token"` when a key was sent and rejected. Ask the human for a new one; do not retry in a loop. |
| 402 | — | The account is out of credits. Tell the human; only they can top up. |
| 404 | `not_found` / `unknown_endpoint` | The resource does not exist, or is not owned by this key. Re-read `GET /v1`. |
| 409 | `limit_reached` | A ceiling was hit (for example, 10 active API keys). Revoke one first. |
| 429 | — | Rate limited. Wait the number of seconds in `Retry-After`, then resume. |
| 500 | `internal_error` | Ours, not yours. Retry once with backoff, then report it. |

The generation endpoints are shared with the website and use a flatter shape,
`{"error": "<message>", "code": "<CODE>"}`. Both are JSON, and both always carry a
human-readable message.

## Revocation

An `agent_auth` token you registered yourself, you revoke yourself, at the
`revocation_uri` from the same metadata block:

```bash
curl -sX POST https://aicontentdrop.com/agent/auth/revoke \
  -H "Content-Type: application/json" \
  -d '{ "token": "…" }'
```

Do that when a run ends or a host is decommissioned, rather than leaving live
credentials behind you.

An `acd_live_` key can be revoked at any time — by the human in
<https://aicontentdrop.com/settings/integrations>, or by us if a key is abused or
leaked. Revocation takes effect immediately, and every subsequent request returns
`401 unauthorized`.

Treat a 401 on a previously working key as final, not transient: stop, tell the
human, and ask for a new key. Retrying a revoked credential is indistinguishable
from an attack. Keys carry no refresh token and no expiry-based rotation; to
rotate, the human creates a new key and revokes the old one.

If you believe a key has leaked, say so at <support@aicontentdrop.com> and stop
using it in the same turn.

## What you can do with no credential at all

No account, no key, no rate-limit negotiation:

| Resource | URL |
|---|---|
| Curated site overview | <https://aicontentdrop.com/llms.txt> |
| Full text of every public article | <https://aicontentdrop.com/llms-full.txt> |
| Markdown twin of any public page | append `.md` to the path |
| Markdown via content negotiation | send `Accept: text/markdown` |
| Sitemaps | `/sitemap.xml`, `/sitemap-tools.xml`, `/sitemap-video.xml` |
| Importable agent skills (SKILL.md, SHA-256 digests) | <https://aicontentdrop.com/.well-known/agent-skills/index.json> |
| Natural-language query (NLWeb) | POST <https://aicontentdrop.com/ask> |
| A2A agent (read-only) | POST <https://aicontentdrop.com/a2a> |
| MCP server (read tools free) | POST <https://aicontentdrop.com/mcp> |
| Agent self-registration (`agent_auth`, read scope) | POST <https://aicontentdrop.com/agent/auth/register> |
| GraphQL API, introspection open | POST <https://aicontentdrop.com/graphql> |
| Sandbox generation (no key, no credits) | `X-Sandbox: true` on POST <https://aicontentdrop.com/v1/generate/video> |
| Model catalogue with credit costs | <https://aicontentdrop.com/v1/models> |

The free tools under <https://aicontentdrop.com/tools> also require no login and no
credits.

Usage terms are declared as Content Signals in `robots.txt`:
`ai-train=yes, search=yes, ai-input=yes` for crawlers that can cite or link back.
Indexing, citing, grounding answers in, and training on this content are all
permitted. A link back is appreciated, not required. The only exception is
training-only crawlers with no citation path (CCBot, Bytespider), which are
disallowed in `robots.txt`.

## Hand-off links for humans

| Intent | Where to send the user |
|---|---|
| Create an account (10 free credits, no card) | <https://aicontentdrop.com/register> |
| Create an API key | <https://aicontentdrop.com/settings/integrations> |
| Sign in to an existing account | <https://aicontentdrop.com/login> |
| Compare plans and prices | <https://aicontentdrop.com/pricing> |
| Start generating in the UI | <https://aicontentdrop.com/chat> |

Accounts support email + password, Google OAuth, and magic-link sign-in. Paid plans
are billed through Polar.sh, our Merchant of Record. Free tier is 10 credits; paid
plans start at $19/month.

## Verifying our crawler

When this service fetches a page or media file from your site, the request is signed
with HTTP Message Signatures (RFC 9421) carrying `Signature-Agent`, `Signature-Input`,
and `Signature` headers, tagged `web-bot-auth`. Our public keys:
<https://aicontentdrop.com/.well-known/http-message-signatures-directory>

So you can tell our crawler apart from anything merely claiming our user-agent string.
If you'd rather we didn't fetch from you at all, a `Disallow` in your robots.txt is
honored.

## Legal

- Terms of use: <https://aicontentdrop.com/terms-of-use>
- Privacy policy: <https://aicontentdrop.com/privacy-policy>
- Abuse reports: <abuse@aicontentdrop.com>
- Legal contact: <legal@aicontentdrop.com>

## Contact

Support: <support@aicontentdrop.com>
Contact page: <https://aicontentdrop.com/contact>
About: <https://aicontentdrop.com/about>
Developers: <https://aicontentdrop.com/developers>
