An API without a machine-readable contract is a rumor. The OpenAPI 3.1 specification at GET /api/v1/openapi is the yas.sh API's source of truth: every endpoint, schema, security scheme, and error format, in one document. This guide shows three ways to consume it — generated clients, hand-written requests, and the human reference — and when each is right.
The spec itself
curl -s https://yas.sh/api/v1/openapi | jq '.info, .paths | keys'
The spec covers the full surface: auth (register, login, me, logout), links (CRUD, duplicate check, CSV export), analytics (overview, per-link), API keys, QR generation, tools, and the redirect contract. Two details worth noting:
- Security schemes:
cookieAuth(theyas_sessioncookie) andbearerAuth(yas_live_...API keys) are declared per-endpoint, so generated clients know exactly what each call needs. - Errors: every endpoint documents the RFC 9457
application/problem+jsonformat with thecode,title,status,detail, andretryAfterfields clients should handle.
Option 1 — Generated clients (TypeScript)
The fastest path to a typed client:
npx openapi-typescript https://yas.sh/api/v1/openapi -o yas-api.d.ts
This produces types for every request/response in the API:
import type { paths } from "./yas-api.d.ts";
// Fully typed fetch helper (or use openapi-fetch)
type CreateLink = paths["/links"]["post"]["requestBody"]["content"]["application/json"];
const body: CreateLink = { originalUrl: "https://example.com", customAlias: "launch" };
const res = await fetch("https://yas.sh/api/v1/links", {
method: "POST",
headers: { Authorization: "Bearer " + process.env.YAS_KEY, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
The win: when the API evolves, npx openapi-typescript re-runs and the compiler finds every integration that needs attention. Spec-driven development keeps clients honest.
Option 2 — Hand-written requests
For scripts and one-offs, plain HTTP is all you need. The API reference documents each endpoint with curl, JavaScript, and TypeScript examples:
# create a link — curl
curl -X POST https://yas.sh/api/v1/links \
-H "Authorization: Bearer yas_live_..." -H "Content-Type: application/json" \
-d '{"originalUrl":"https://example.com","customAlias":"launch"}'
// list links — TypeScript
const res = await fetch("https://yas.sh/api/v1/links?limit=20", {
headers: { Authorization: "Bearer " + process.env.YAS_KEY },
});
const { data, nextCursor } = await res.json();
The patterns that matter in any client: send Content-Type: application/json on writes, set Authorization: Bearer <key>, include an Idempotency-Key for retries, and handle 429 with retryAfter (rate limits).
Option 3 — The human reference
The rendered documentation at /docs/api walks every endpoint with the same examples, plus the operational details that specs don't capture: which endpoints are public, which are rate-limited, and the exact error codes with their meanings. When the spec and the page disagree, the spec wins — and they're maintained together, so they don't disagree.
Keeping integrations in sync
The discipline that prevents drift:
- Pin the spec — cache a copy in your repo (
curl -o spec.json https://yas.sh/api/v1/openapi) so builds are reproducible. - Regenerate on upgrade — re-run openapi-typescript whenever you bump integration code; let the compiler find breaking changes.
- Test against the contract — a smoke test that hits the documented endpoints (like the platform's own smoke suite) catches drift before users do.
- Watch the changelog — the changelog notes API changes in each release.
Conclusion
The OpenAPI spec turns the yas.sh API from documented into provable: generated types, contract-tested integrations, and examples that match the implementation. Start with the spec endpoint, skim the human reference, and generate your first typed client this afternoon.
