# RaftLabs Developer Portal

> API documentation, versioning policy, and rate limits for the public HTTP surface at raftlabs.com. No keys, no signup, no cost.

- API description: https://www.raftlabs.com/openapi.json
- Current version: 1 (`/api/v1`)
- HTML version of this page: https://www.raftlabs.com/developers

## Authentication

- There are no API keys. Every endpoint in the OpenAPI document is public, unauthenticated, and read-only.
- Nothing on this API creates, changes, or bills anything, so there is no credential to issue and no key rotation to manage.
- There is no API for starting a project or requesting a quote. Those go through a human — the contact form or hi@raftlabs.com.

## Endpoints

### `GET /api/health`

Service health

Confirms the site is reachable. Permanent unversioned alias of /api/v1/health, kept because /.well-known/api-catalog, /llms.txt and external monitors already reference it. Not deprecated; it always mirrors the current version. Requires no authentication. Reports reachability only — no dependency values are returned.

Full schema: https://www.raftlabs.com/openapi.json

### `GET /api/v1/health`

Service health

Confirms the site is reachable. This is the canonical, versioned path. Requires no authentication. Reports reachability only — no dependency values are returned.

Full schema: https://www.raftlabs.com/openapi.json

## Quickstart

### Check the service is reachable

The one endpoint that is safe to call as often as you like. No side effects, no credential, no cost.

```bash
curl -i https://www.raftlabs.com/api/v1/health
```

### Read the API description

Start here in a client generator. The document carries the schemas, the error catalog, and the versioning policy.

```bash
curl -s https://www.raftlabs.com/openapi.json | jq '.paths | keys'
```

### See the rate-limit budget

Every response reports the remaining budget, on success as well as on refusal, so a client can pace itself before it is ever throttled.

```bash
curl -sI https://www.raftlabs.com/api/v1/health | grep -i ratelimit
```

### Inspect an error

Every failure is RFC 9457 problem details with a stable `code` and an actionable `hint`. No route under /api returns an HTML error page.

```bash
curl -s https://www.raftlabs.com/api/does-not-exist | jq
```

### Get a page as markdown

Content is published for agents as well as browsers. Unknown paths answer 404 with a markdown recovery body listing the site's own indexes.

```bash
curl -s -H "Accept: text/markdown" https://www.raftlabs.com/
```

## Sandbox

- There is no separate sandbox host. `/api/v1/health` is the sandbox: it is idempotent, free, side-effect free, and served from the same infrastructure as everything else, so a client validated against it is validated against production.
- Point a generated client at it, confirm you parse the rate-limit headers and the RFC 9457 problem body, then move on.
- Request an unregistered path such as `/api/does-not-exist` to exercise your error handling against a real problem+json response.

## Versioning

The major version lives in the URL path. `/api/v1` is current, and every response carries `API-Version: 1`.

A breaking change ships under a new prefix. It never lands on an existing one.

**Breaking — needs a new version:**

- Removing an endpoint, or removing a field from a response.
- Renaming a field, or changing its type.
- Adding a required request field, or tightening validation on an existing one.
- Changing the HTTP status or the `code` returned for an existing condition.

**Non-breaking — may ship inside a version at any time:**

- Adding an endpoint.
- Adding an optional request field.
- Adding a field to a response — parse defensively and ignore what you do not use.
- Adding a new `code` value for a condition that previously had none.

**Unversioned aliases.** Permanent aliases of the current version, kept because external monitors and machine-readable indexes already reference them. Not deprecated.

- `/api/health` → `/api/v1/health`

## Deprecation policy

An endpoint gets at least 180 days between the deprecation notice and the sunset date. It keeps answering normally for the whole notice period.

A retirement is signalled in all of these places at once:

- `Deprecation` response header (RFC 9745) carrying the date the deprecation took effect, as a structured-field date: `Deprecation: @1780272000`.
- `Sunset` response header (RFC 8594) carrying the date the endpoint stops answering, as an HTTP-date.
- `Link` response header with `rel="successor-version"` pointing at the replacement.
- `deprecated: true` on the operation in /openapi.json, with the successor named in its description.
- An entry in the deprecations table on /developers, listing the sunset date.

**Active deprecations: none.** No endpoint on this API is scheduled for retirement.

## Rate limits

| Policy | Limit | Window |
| --- | --- | --- |
| `health` | 120 requests | 60s per client |
| `not-found` | 60 requests | 60s per client |

Every response carries the live budget:

| Header | Meaning |
| --- | --- |
| `RateLimit-Limit` | Requests permitted in the current window. |
| `RateLimit-Remaining` | Requests left in the current window. |
| `RateLimit-Reset` | Seconds until the window resets. |
| `RateLimit-Policy` | Structured field naming the policy, its quota and its window: `"health";q=120;w=60`. |
| `RateLimit` | Structured field carrying the live state: `"health";r=119;t=60`. |
| `Retry-After` | Seconds to wait. Sent only with a 429, where RFC 9110 gives it meaning. |

Quotas are counted per serverless instance and per client IP, so the reported budget is a floor rather than a global guarantee. Treat a 429 as authoritative and honour `Retry-After`.

## Errors

Every failure is RFC 9457 problem details, `application/problem+json`, with two extension members: `code` (stable machine identifier) and `hint` (what to change before retrying). No route under /api returns an HTML error page.

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_request_body` | 400 | Invalid request body |
| `missing_required_field` | 400 | Missing required field |
| `captcha_verification_failed` | 403 | Captcha verification failed |
| `not_found` | 404 | Endpoint not found |
| `method_not_allowed` | 405 | Method not allowed |
| `rate_limit_exceeded` | 429 | Rate limit exceeded |
| `server_misconfigured` | 500 | Server misconfigured |
| `upstream_error` | 502 | Upstream service failed |
| `internal_error` | 500 | Internal server error |

## Machine-readable resources

- [OpenAPI 3.1 description](https://www.raftlabs.com/openapi.json) (`application/json`): Every public endpoint, its request and response schemas, error codes, and the versioning policy under `x-api-lifecycle`.
- [API catalog](https://www.raftlabs.com/.well-known/api-catalog) (`application/linkset+json`): RFC 9727 linkset pointing at every agent-facing resource on this site.
- [Agent Skills index](https://www.raftlabs.com/.well-known/agent-skills/index.json) (`application/json`): Task-oriented guides: browse the portfolio, request a quote, understand the services.
- [llms.txt](https://www.raftlabs.com/llms.txt) (`text/plain`): Curated map of the site, written for LLMs.
- [llms-full.txt](https://www.raftlabs.com/llms-full.txt) (`text/plain`): The full content corpus in one file.
- [pricing.md](https://www.raftlabs.com/pricing.md) (`text/markdown`): Engagement models and price bands as structured markdown.
- [sitemap.xml](https://www.raftlabs.com/sitemap.xml) (`application/xml`): Every indexable URL.
- [JSON Feed](https://www.raftlabs.com/feed.json) (`application/feed+json`): New articles, also served as /rss.xml and /atom.xml.

## Support

- Email: team@raftlabs.com
- Contact form: https://www.raftlabs.com/contact-us
- Report a problem with this API, or ask for a higher allowance, by email. Include the request path, the `code` you received, and the time.
