# info.summary
Public JSON API for jordandalton.com: services and pricing, published articles, and build day bookings.

# info.description
Public, unauthenticated JSON API for jordandalton.com. No API key is required.

## Versioning

The API is versioned in the URL path: every endpoint lives under `/api/v1/`. A breaking change ships as a new path prefix (`/api/v2/`); the previous version keeps working alongside it.

## Deprecation policy

When a version is scheduled for removal its responses carry the `Deprecation` header (RFC 9745) and a `Sunset` header (RFC 8594) with the removal date, at least 6 months in advance. `GET /api/v1/health` also reports `deprecated` and `sunset` fields so an agent can check without parsing headers. Version 1 is current and is not deprecated.

## Rate limits

Every response carries `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `RateLimit-Policy`. The current quota is 60 requests per minute per IP. A `429` response carries `Retry-After` with the number of seconds to wait.

## Errors

Every 4xx and 5xx response is `application/problem+json` (RFC 9457) using the `Problem` schema: a machine-readable `code`, a human-readable `message`, and a `hint` describing how to recover.

## MCP

The same capabilities are exposed as MCP tools and resources over Streamable HTTP at `https://jordandalton.com/mcp`.

# tags[].description
Service catalog, pricing, and availability.

# paths./api/v1/health.get.description
Returns whether the API is serving traffic, which version is current, and whether that version is deprecated or scheduled for sunset. Call this before a batch of requests to confirm the version you integrated against is still supported.

# paths./api/v1/services.get.summary
List services with pricing and availability

# paths./api/v1/services.get.description
Returns every service Jordan offers — the fit check, full and half build days, Agent Ready, and the fractional CAIO/CTO retainers — with price, duration, and a landing page URL, plus availability, payment terms, and token budget in the meta object.

# paths./api/v1/articles.get.description
Returns every published article, newest first, with slug, title, description, publish date, and canonical URL. Expired articles are excluded.

# paths./api/v1/articles/{slug}.get.summary
Get one published article, including its markdown body

# paths./api/v1/articles/{slug}.get.description
Returns a single article by slug with the full markdown body. Get valid slugs from listArticles.

# paths./api/v1/articles/{slug}.get.parameters[].description
The article slug, as returned by listArticles.

# paths./api/v1/bookings.post.description
Sends a booking request straight to Jordan as an SMS. No authentication or CSRF token is required. Jordan replies to the supplied email address, normally within 24 hours. Submitting does not reserve a slot; the reply confirms the date.

# paths./book.post.summary
Submit a booking from the website form (deprecated)

# paths./book.post.description
The session-backed endpoint behind the booking form on the website. It redirects browsers and requires a CSRF token. Agents should call createBooking at /api/v1/bookings instead.

# paths./book.post.responses.302.description
Browser submission: redirects back to the form with a flash message.

# paths./up.get.description
Laravel health endpoint. Returns 200 with an HTML body while the application is serving traffic. Agents should prefer getApiHealth, which returns JSON.

# components.schemas.Service.properties.price.description
Price as displayed, including currency and any add-ons.

# components.schemas.ServiceListEnvelope.description
The service catalog plus the terms that apply to all services.

# components.schemas.ServiceListEnvelope.properties.meta.properties.availability.description
How many build days are open and how far out.

# components.schemas.ServiceListEnvelope.properties.meta.properties.ip_transfer.description
Whether all IP transfers to the client on completion.

# components.schemas.ServiceListEnvelope.properties.meta.properties.token_budget.description
Typical LLM token spend, billed to the client provider accounts.

# components.schemas.ServiceListEnvelope.properties.meta.properties.api_keys_required.description
Provider accounts the client needs to supply.

# components.schemas.Article.properties.slug.description
URL-safe identifier, unique per article.

# components.schemas.ArticleDetail.allOf[].properties.markdown.description
The full article body as markdown, frontmatter removed.

# components.schemas.HealthEnvelope.properties.data.properties.status.description
Always "ok" when the API is serving traffic.

# components.schemas.HealthEnvelope.properties.data.properties.api_version.description
The version prefix this response came from.

# components.schemas.HealthEnvelope.properties.data.properties.deprecated.description
True once this version is scheduled for removal.

# components.schemas.HealthEnvelope.properties.data.properties.sunset.description
Removal date once one is set, otherwise null.

# components.schemas.BookingRequest.properties.brief.description
What needs building. One paragraph is enough.

# components.schemas.Problem.description
RFC 9457 problem detail. Every 4xx and 5xx response uses this shape.

# components.schemas.Problem.properties.type.description
Documentation anchor for this error code.

# components.schemas.Problem.properties.title.description
Short, human-readable summary of the error type.

# components.schemas.Problem.properties.status.description
The HTTP status code, repeated in the body.

# components.schemas.Problem.properties.detail.description
What went wrong with this specific request.

# components.schemas.Problem.properties.code.description
Stable machine-readable error code. Branch on this, not on the message.

# components.schemas.Problem.properties.message.description
Same text as detail, at the top level for convenience.

# components.schemas.Problem.properties.error.description
The error, its code, and how to recover from it.

# components.schemas.Problem.properties.error.properties.message.description
Human-readable description of the failure.

# components.schemas.Problem.properties.error.properties.fields.description
Present only on validation_failed: field name to list of problems.

# components.schemas.ValidationProblem.allOf[].properties.errors.description
Field name to the list of validation messages for that field.

# components.responses.NotFound.description
No resource exists at this path. code: not_found.

# components.responses.ValidationFailed.description
One or more fields failed validation. code: validation_failed.

# components.responses.RateLimited.description
Rate limit exceeded. code: rate_limited. Wait the number of seconds in Retry-After.

# components.responses.ServerError.description
Something failed on the server. code: server_error. Retry once with backoff.

# components.headers.RateLimit-Policy.description
The quota name, its size (q), and its window in seconds (w).