⚡ Promptolis Original · Coding & Development

🌐 API Endpoint Designer

REST vs RPC decision, resource naming consistent with your codebase, error response schema — plus the 2 endpoints that seem needed but create coupling debt.

⏱️ 5 min to design 🤖 ~90 seconds in Claude 🗓️ Updated 2026-04-19

Why this is epic

Most API design tools produce 'best practice' endpoints that don't match your existing style. This Original designs endpoints CONSISTENT with your codebase — including your error schema, auth patterns, and naming conventions.

Flags the 2 endpoints in every feature spec that LOOK useful but actually create coupling debt — the hidden tech debt that ships as 'just one more endpoint' and becomes unmaintainable.

Resolves the REST-vs-RPC decision per endpoint, not per project. Some operations genuinely don't fit REST (e.g., `POST /users/{id}/reset-password`); naming them awkwardly makes the API worse, not better.

The prompt

Promptolis Original · Copy-ready
<role> You are a staff backend engineer who has designed APIs at multiple scales — from startup v1 to enterprise systems serving billions of requests. You know the difference between APIs that age well and APIs that collapse under their own weight by year 3. You care more about consistency than cleverness. You will tell the user when their proposed endpoint creates coupling debt even if the endpoint itself is technically fine. </role> <principles> 1. API consistency within a codebase matters more than consistency with external 'best practices.' Match what exists. 2. REST is the default but not the only answer. Some operations (reset password, publish, trigger) don't fit REST cleanly — use RPC-style for those. 3. Every endpoint has a coupling cost. 'Just one more endpoint' that touches N resources creates debt. 4. Error responses must be consistent across the entire API. Inconsistent error schema is the #1 source of client-code fragility. 5. URL design is harder to change than response shape. Get URLs right upfront. 6. Versioning strategy must be decided BEFORE v1 ships, not when v2 is needed. 7. Internal APIs and public APIs have different design constraints. Clarify which early. </principles> <input> <feature-spec>{what you're building — user-facing description of the feature and the operations users/clients need to perform}</feature-spec> <existing-api-examples>{paste 3-5 existing endpoints from your codebase — URL, method, request shape, response shape. This is how the Original learns your style.}</existing-api-examples> <error-schema>{your standard error response — status codes, error body shape, required fields}</error-schema> <auth-pattern>{how authentication works in your API — Bearer, cookies, API keys, OAuth}</auth-pattern> <api-audience>{internal service-to-service, public external, first-party client only, mixed}</api-audience> <constraints>{anything that must be respected — existing client code, rate limits, backward compat, specific performance requirements}</constraints> </input> <output-format> # API Design: [Feature name] ## Style Calibration One paragraph. What style signals I pulled from your existing endpoints. How this design matches. ## The Endpoints For each endpoint: ### `METHOD /path/template` - **Intent:** What user action this serves (not 'what resource this returns') - **Request:** Headers + body shape - **Response:** Success shape + status codes - **Error cases:** Specific errors this endpoint can return, in your standard schema - **Auth:** Which permission level required - **Idempotency:** If applicable ## The REST-vs-RPC Decisions For each endpoint, which paradigm. If an RPC-style endpoint exists, explicitly justify why it's not shoehorned into REST. ## The 2 Endpoints That Seem Needed But Create Coupling Debt Specific endpoints from the spec that LOOK helpful but shouldn't exist because: - They tightly couple unrelated resources - They duplicate business logic better kept in clients - They create pagination/filtering complexity - Or they'll need breaking changes within 12 months Replace with: the right shape (usually a combination of existing endpoints or a different abstraction). ## Error Response Schema for These Endpoints List of specific error conditions across the endpoints with the corresponding error codes and bodies — consistent with your existing schema. ## Versioning Considerations If these endpoints might evolve, the 2 most likely changes and the versioning strategy that handles them. ## For the OpenAPI Spec Quick YAML/JSON outline structure for the endpoints. Not the full spec — just the skeleton so your team can fill in the details. ## Key Takeaways 5 bullets about API design applicable beyond this specific feature. </output-format> <auto-intake> If input is incomplete, ask in one message: 1. 'What's the feature? Describe the user-facing operations, not the data model.' 2. 'Paste 3-5 existing endpoints from your codebase so I can match your style.' 3. 'What's your standard error response schema?' 4. 'Auth pattern — Bearer, cookies, API keys, OAuth?' 5. 'Internal, public, or mixed audience?' 6. 'Any constraints — backward compat, rate limits, performance?' Don't design APIs without seeing existing endpoints. The internet's 'best practice' ignores your actual conventions. </auto-intake> Now, design the endpoints:

Example: input → output

Here's how this prompt actually performs. Real input below, real output from Claude Opus 4.

📝 Input
<feature-spec>Users should be able to create 'reading lists' — curated collections of articles from our main content system. They can create multiple lists, add/remove articles, share lists publicly (with a shareable URL), and optionally let others suggest articles to their lists (which the owner approves or rejects). Articles exist in our main content system with their own resource URLs.</feature-spec>
<existing-api-examples>
1. `GET /api/v1/articles/{slug}` → returns article { id, slug, title, content_html, author, published_at }
2. `POST /api/v1/articles` with body { title, content_md } → 201 { id, slug, ... }
3. `PATCH /api/v1/articles/{slug}` with body { title?, content_md? } → 200 { id, slug, ... }
4. `GET /api/v1/users/{id}` → returns user { id, handle, display_name, created_at }
5. `DELETE /api/v1/articles/{slug}` → 204
</existing-api-examples>
<error-schema>All errors return JSON: { error: { code: string, message: string, details?: object } } with appropriate HTTP status. Codes use snake_case like 'not_found', 'unauthorized', 'validation_failed'.</error-schema>
<auth-pattern>Bearer tokens in Authorization header. JWT contains user_id and scope array.</auth-pattern>
<api-audience>Mixed — first-party web/mobile clients AND a public developer API. Public devs can read articles but not create/modify.</api-audience>
<constraints>Backward compat critical (existing clients shipping). Slug-based URLs are established convention. Must support pagination (existing endpoints use cursor-based).</constraints>
🤖 Output

API Design: Reading Lists Feature

An API design is a contract between clients and server that must be consistent with existing patterns, honest about coupling costs, and designed to evolve over multiple years. For the reading list feature, the key decisions are (1) how to name resources consistently with your existing slug convention, (2) how to handle the suggestion workflow without creating RPC-style endpoints that break your REST pattern, and (3) which endpoints in the obvious list actually create coupling debt and should NOT exist.

Style Calibration

Your existing API uses kebab-case paths, slug-based resource identification, and snake_case in JSON bodies. Resources are hierarchical (/articles/{slug}, /users/{id}). You mix numeric IDs (users) with slugs (articles) — this works because articles have stable human-readable slugs and users don't. For reading lists, I'll use slugs for public sharing and numeric IDs for internal references, which matches your existing pattern. Your versioning is URL-path based (/api/v1/). Your error schema is consistent and well-designed.

The Endpoints

`GET /api/v1/users/{user_id}/reading-lists`
  • Intent: List a user's reading lists. Own lists + public lists of others.
  • Request: Authorization: Bearer <token>, query params: ?cursor=xxx&limit=20
  • Response: 200 { lists: [{ id, slug, name, description, is_public, article_count, created_at }], next_cursor }
  • Error cases: 401 unauthorized (missing/invalid token), 404 not_found (user doesn't exist)
  • Auth: Own lists — all auth users. Public lists of others — any auth user including public devs.
  • Idempotency: Safe, cacheable.
`POST /api/v1/reading-lists`
  • Intent: Create a new reading list for the authenticated user.
  • Request: Authorization: Bearer <token>, body: { name, description?, is_public? } (is_public defaults to false)
  • Response: 201 { id, slug, name, description, is_public, article_count: 0, created_at, owner: { id, handle } }
  • Error cases: 401 unauthorized, 422 validation_failed (name too short/long, duplicate name per user if we enforce)
  • Auth: Any authenticated user. Not available to read-only public devs.
`GET /api/v1/reading-lists/{slug}`
  • Intent: Get a single reading list with its articles.
  • Request: Authorization: Bearer <token>, optional ?include=articles
  • Response: 200 { id, slug, name, description, is_public, owner, article_count, articles: [...], created_at }
  • Error cases: 404 not_found (doesn't exist or private list not owned by caller)
  • Auth: Any auth user for public lists; only owner for private lists. Public devs: public only.
  • Note: Use 404 instead of 403 for private lists to avoid revealing existence.
`PATCH /api/v1/reading-lists/{slug}`
  • Intent: Update name, description, or visibility.
  • Request: Authorization: Bearer <token>, body: { name?, description?, is_public? }
  • Response: 200 { id, slug, name, description, is_public, ... }
  • Error cases: 401 unauthorized, 403 forbidden (not owner), 404 not_found, 422 validation_failed
  • Auth: Owner only.
  • Idempotency: Yes.
`DELETE /api/v1/reading-lists/{slug}`
  • Intent: Delete a reading list.
  • Response: 204 No Content
  • Error cases: 401, 403 forbidden, 404 not_found
  • Auth: Owner only.
`PUT /api/v1/reading-lists/{slug}/articles/{article_slug}`
  • Intent: Add an article to the list. Idempotent (re-adding is a no-op, not an error).
  • Request: Authorization: Bearer <token>, no body required, optional { note } for user annotation
  • Response: 204 No Content (article was added or already present)
  • Error cases: 401, 403 forbidden (not owner), 404 not_found (list or article doesn't exist)
  • Auth: Owner only. For suggestions workflow, see below.
  • Idempotency: Yes — this is why PUT not POST.
`DELETE /api/v1/reading-lists/{slug}/articles/{article_slug}`
  • Intent: Remove an article from the list.
  • Response: 204
  • Error cases: Standard.
  • Auth: Owner only.
`POST /api/v1/reading-lists/{slug}/suggestions`
  • Intent: Someone other than the owner suggests an article for this list. Owner must approve.
  • Request: Body: { article_slug, note? }
  • Response: 201 { suggestion_id, status: 'pending', suggested_by: { id, handle }, article_slug, note, created_at }
  • Error cases: 401, 403 forbidden (list doesn't accept suggestions, or caller is the list owner trying to suggest to themselves), 404 not_found, 409 conflict (article already suggested by this user)
  • Auth: Any auth user who isn't the owner.
`POST /api/v1/reading-lists/{slug}/suggestions/{suggestion_id}/approve`
`POST /api/v1/reading-lists/{slug}/suggestions/{suggestion_id}/reject`
  • Intent: Owner approves or rejects a pending suggestion. These are RPC-style because they're state transitions, not resource manipulation.
  • Request: No body (or { note } for reject).
  • Response: 200 { suggestion_id, status: 'approved' | 'rejected' }. On approve, also adds the article to the list.
  • Error cases: 401, 403 forbidden (not owner), 404 not_found, 409 conflict (already processed)
  • Auth: Owner only.
  • Idempotency: Yes — approving already-approved is a no-op 200.

The REST-vs-RPC Decisions

All resource CRUD operations use REST verbs (GET, POST, PUT, PATCH, DELETE).

The two RPC-style endpoints — /suggestions/{id}/approve and /suggestions/{id}/reject — are justified because they represent state transitions on a resource, which REST doesn't handle cleanly. Alternative would be PATCH /suggestions/{id} with body { status: 'approved' } — valid REST, but: (1) it lets a client set the status to arbitrary values that your server must validate, and (2) /approve + /reject make the allowed transitions explicit in the URL and matches how your business logic actually works. Your existing API has no precedent either way, so we're setting the pattern.

The 2 Endpoints That Seem Needed But Create Coupling Debt

❌ `GET /api/v1/articles/{article_slug}/reading-lists` (DO NOT BUILD)

Why it looks useful: 'Show me which reading lists contain this article.'

Why it's debt: This endpoint tightly couples articles to reading lists — every article page must now know about reading lists. If you ever remove or rename the reading-list feature, every article-detail page breaks. Also, this endpoint has unclear pagination semantics (what's the cursor? list ID?) and ambiguous auth (do I see private lists that contain this article if I'm in them?).

Build instead: GET /api/v1/reading-lists?contains_article={slug} — a filtered list query. This keeps the articles API unaware of reading lists and gives clean pagination from the existing lists endpoint.

❌ `POST /api/v1/reading-lists/bulk-actions` (DO NOT BUILD)

Why it looks useful: 'Let users reorder/delete multiple articles in one request.'

Why it's debt: Bulk actions endpoints are a classic coupling trap. They start simple ('just reorder 3 articles') and within 6 months grow to: bulk add, bulk remove, bulk move between lists, bulk privacy-change. Each new action bloats the endpoint, error responses become complex (partial success?), and it becomes the tech-debt endpoint your team maintains forever.

Build instead: Use the individual endpoints with client-side batching if needed. For actual performance-critical bulk operations, add a specific RPC endpoint (POST /reading-lists/{slug}/reorder with body { article_slugs: [...] }) — not a generic bulk-actions endpoint.

Error Response Schema for These Endpoints

Error codeHTTPWhen
`unauthorized`401Missing or invalid token
`forbidden`403Authenticated but not allowed (not owner, read-only API key trying to write)
`not_found`404Resource doesn't exist OR private list not visible to caller
`validation_failed`422Request body failed validation; `details: { field: reason }`
`conflict`409Suggestion already exists, suggestion already processed
`rate_limit_exceeded`429If/when rate limits apply

All follow your existing schema: { error: { code, message, details? } }.

Versioning Considerations

The two most likely evolutions of this API:

1. Adding collaboration (multiple owners per list). Handle this by keeping `owner` as a single-value field in v1 responses, but building the data model with many-to-many. When you ship v2, `owner` can become `owners: [...]` with a dual-response period.

2. Adding tags/sections within a list. Don't build this into v1. When it ships, add `section_id?` to the article-in-list shape — backward compatible as long as existing clients ignore unknown fields (which they should).

Both changes can be handled within /api/v1/ with response-shape evolution rather than a v2 URL bump. The only change that would force a v2 is if you dropped the slug-based URLs, which is unlikely.

For the OpenAPI Spec

paths:
  /users/{user_id}/reading-lists:
    get: { summary: 'List a user\'s reading lists' }
  /reading-lists:
    post: { summary: 'Create reading list' }
  /reading-lists/{slug}:
    get: { summary: 'Get reading list' }
    patch: { summary: 'Update reading list' }
    delete: { summary: 'Delete reading list' }
  /reading-lists/{slug}/articles/{article_slug}:
    put: { summary: 'Add article to list (idempotent)' }
    delete: { summary: 'Remove article from list' }
  /reading-lists/{slug}/suggestions:
    post: { summary: 'Suggest an article' }
  /reading-lists/{slug}/suggestions/{suggestion_id}/approve:
    post: { summary: 'Approve suggestion' }
  /reading-lists/{slug}/suggestions/{suggestion_id}/reject:
    post: { summary: 'Reject suggestion' }

Key Takeaways

  • Consistency with YOUR codebase beats consistency with the internet. Match existing patterns.
  • RPC-style endpoints are fine when they represent state transitions. Don't shoehorn every operation into REST.
  • Use 404 instead of 403 for private resources to avoid revealing existence — especially important for user-owned resources.
  • Resist 'just one more endpoint' syndrome. The 2 coupling-debt endpoints you avoided here will save you 40+ hours over the next 2 years.
  • Idempotency matters on PUT operations. Clients will retry; your endpoints must handle it cleanly.

Common use cases

  • Backend engineers designing a new feature's API surface
  • Tech leads reviewing a junior dev's API proposal
  • Startup founders designing v1 APIs that need to evolve without breaking in year 2
  • Teams migrating from monolith to microservices who need to decide service boundaries
  • Platform engineers designing internal service-to-service APIs
  • Consultants auditing an existing API and proposing improvements
  • Engineers writing the OpenAPI spec BEFORE building the service

Best AI model for this

Claude Opus 4 or GPT-5 Thinking. API design requires holding multiple concerns in mind (consistency, coupling, evolution path, security) — mid-tier models miss the coupling-debt detection which is the highest-value output.

Pro tips

  • Always paste 3-5 existing endpoints from your codebase. The Original matches YOUR style — not the internet's idea of 'best practice.'
  • Include your current error response schema. Consistent error handling matters more than clever endpoint design.
  • Specify your auth pattern (Bearer tokens, session cookies, API keys, OAuth). It affects how you design the endpoints.
  • List the ACTUAL user intents, not the data models. 'User wants to share a playlist' drives better design than 'endpoint for playlist sharing.'
  • For internal APIs, be explicit about your service-mesh/RPC infrastructure. gRPC and REST have different design patterns.
  • Don't skip the 'coupling debt' section. Those endpoints are the ones your future-self will curse.

Customization tips

  • Always paste 3-5 existing endpoints — the Original matches YOUR style. Without them, you get generic REST-101 design that clashes with your codebase.
  • Be honest about your audience. Internal-only APIs can have different conventions than public APIs. The design changes significantly.
  • For features that involve state transitions (approve, publish, submit, deliver), expect and welcome RPC-style endpoints. Forcing them into REST produces ugly APIs.
  • Save the coupling-debt section outputs across projects. You'll notice patterns in WHICH endpoints your team tends to over-design.
  • For v1 APIs specifically: decide versioning strategy BEFORE shipping. URL-based (/v1/) is simplest; header-based is more flexible but more complex. Picking later is painful.

Variants

GraphQL Mode

For GraphQL schemas instead of REST — designs the types, mutations, and queries, plus the N+1 concerns specific to GraphQL that REST avoids.

Versioning Strategy

For teams about to ship v2 of an API. Designs the endpoints PLUS the deprecation path, header-based vs URL-based versioning choice, and the migration timeline.

Event-Driven / Webhook

For features that need webhook or event-streaming rather than request-response. Designs the event schema, delivery guarantees, and retry/backoff policy.

Frequently asked questions

How do I use the API Endpoint Designer prompt?

Open the prompt page, click 'Copy prompt', paste it into ChatGPT, Claude, or Gemini, and replace the placeholders in curly braces with your real input. The prompt is also launchable directly in each model with one click.

Which AI model works best with API Endpoint Designer?

Claude Opus 4 or GPT-5 Thinking. API design requires holding multiple concerns in mind (consistency, coupling, evolution path, security) — mid-tier models miss the coupling-debt detection which is the highest-value output.

Can I customize the API Endpoint Designer prompt for my use case?

Yes — every Promptolis Original is designed to be customized. Key levers: Always paste 3-5 existing endpoints from your codebase. The Original matches YOUR style — not the internet's idea of 'best practice.'; Include your current error response schema. Consistent error handling matters more than clever endpoint design.

Explore more Originals

Hand-crafted 2026-grade prompts that actually change how you work.

← All Promptolis Originals