Skip to content
Guides

OpenAPI Spec Best Practices for Integrations and Tools

In this video

Take two OpenAPI specs that describe exactly the same API: same endpoints, same data. Feed both to anything that reads a spec, and one produces operations called "Post Contacts", "Post Contacts 1" and "Get Contacts Id", with free-text boxes everywhere. The other produces "Create a contact", "Get a contact" and "Search contacts", with a dropdown for Lifecycle Stage and a login form already set up.

The difference is an afternoon of work on the spec, and none of it changes how your API behaves. Docs sites, SDK generators, AI agents and n8n node builders all read the same handful of fields. The same fields shape a Make custom app built from your spec and a Zapier integration generated from it. The same fields shape a Make custom app built from your spec and a Zapier integration generated from it. Get those right once, and every one of them improves at the same time.

This guide is for anyone who owns an API, with an n8n node as the running example.

operationId is a name

/contacts:
  post:
    operationId: createContact

operationId is the operation's name, and tools treat it as one. An SDK makes it a method name, acme.createContact(). Docs use it in links. A node builder uses it to tell operations apart.

Leave it out, and every tool invents one from the path and method. That's where names like "post contacts" come from.

Two rules:

  • A verb and a thing, like createContact or listDeals, unique across the spec.
  • Never rename a published one. Other people's code is built on that name, so renaming it is a breaking change, even though your API didn't change at all.

summary is the label people see

    summary: Create a contact
    description: >
      Creates a contact. If the email already exists,
      returns 409 and the existing contact's ID.

summary is the short label. It shows up in the docs sidebar, in an n8n node's operation list, and in the list of tools an AI agent chooses from. Write it the way a button would: start with the verb, keep it short, and leave out IDs and paths.

The longer explanation goes in description: what happens when the email is taken, what the limits are. Keep the two apart. A summary with three sentences in it gets cut off everywhere it's shown.

tags are your resources

Tags group operations. In docs they're the sections. In an n8n node they usually become the resources, the first dropdown where the user picks Contacts or Deals.

  • Name them as things, plural, the way your customers say them: Contacts, Deals, Invoices.
  • Give each operation one tag. Several are allowed, and docs handle that fine, but a tool building a menu has to put each operation somewhere, and with three tags it has to guess.

Say what a field can be

Every field says its type. Most specs stop there. Three small additions change a lot:

Contact:
  type: object
  required: [email]
  properties:
    email:
      type: string
      format: email
      example: asha@example.com
    lifecycleStage:
      type: string
      enum: [lead, customer, churned]
      description: Where the contact is in your sales process.
  • required. If a field has to be there, list it. Tools mark it as required in the form, so users find out before they send, not after.
  • enum. If a field only takes a few values, list them. A tool can turn that into a dropdown. Without it, your user types "Customer" with a capital C and gets an error.
  • format, like email, date-time or uri. A form can check it, and an AI agent filling in the field knows what shape you want.

Add a short description to any field whose name doesn't say it all, and an example. Examples are the most useful thing in a spec that nobody writes.

Say how to log in

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
security:
  - bearerAuth: []

That says "send a bearer token in the Authorization header, on every request." With it, a tool can build the login form for you. Without it, someone has to read your docs to find the header name, whether there's a "Bearer" prefix, and where the key goes, and set it up by hand. That's the 401 from Part 4 of the n8n node series.

If you use OAuth2, put the authorization URL, token URL and scopes in the security scheme too. Scopes are the hardest part of OAuth2, and this is the one place they're written down.

Pagination: the gap in OpenAPI

This one is different, because there's no good answer inside the standard.

You look at a list endpoint and see a cursor: a parameter called cursor, and a field called next in the response. Obviously you send one back as the other. OpenAPI has no way to say that. There's no pagination field in 3.0, 3.1 or 3.2. To a tool, those are two unrelated strings.

So a tool either guesses or it doesn't. A careful one fetches one page and tells you, which is safe, and your user only ever sees the first fifty records.

What you can do:

  • Use one paging pattern on every list endpoint, with the same parameter and field names.
  • Say how it works in the description, in a sentence: "Pass the next value from the response as cursor to get the next page; next is empty on the last page."
  • If the tool you're targeting has its own extension field for pagination, use it.

Part 5 of the series shows what the paging loop looks like when you write it by hand.

Document errors, and lint the spec

Two quick ones.

Document your errors, not only the success response: the 401, the 404, the 409 when the email exists, the 429 when someone's going too fast. A tool can turn those into real error messages instead of "request failed".

Run a linter on the spec. Spectral and Redocly both have a free one:

npx @stoplight/spectral-cli lint openapi.yaml
npx @redocly/cli lint openapi.yaml

They catch missing operationIds and duplicate names before any tool has to.

Recap

  • operationIds that are names, and never change
  • summaries that read like buttons, with the detail in description
  • one tag per operation, named as things
  • required, enum, format and examples on your fields
  • a security scheme, with scopes if you use OAuth2
  • pagination written down, because the standard won't do it for you
  • errors documented, and the spec linted

If you'd like to see what a spec turns into when you build the integration by hand, the Build your own n8n node series goes from an empty folder to a verified node.

One more thing. Everything in this guide is what NativeShip, the tool we build, reads when it turns a spec into an n8n node. When we imported a test spec, it found every resource, every operation and the bearer login from the spec alone. On the list endpoints it couldn't be sure about, it kept them to one page and said so, instead of guessing.

Frequently asked questions

A verb and a thing, like createContact or listDeals, unique across the spec. Treat it as a public name: once published, renaming it breaks code that other people generated from your spec.

summary is a short label, like a button: "Create a contact". description is the longer explanation: behaviour, limits and edge cases. Tools show the summary in menus and lists, where long text gets cut off.

No. OpenAPI 3.0, 3.1 and 3.2 have no standard pagination field. Use the same paging pattern and names on every list endpoint, and describe how it works in the description; some tools also read their own extension field.

Spectral (@stoplight/spectral-cli) and Redocly CLI (@redocly/cli) both have a free lint command that catches missing operationIds, duplicate names and other common gaps.

Ready to launch your n8n, Make, Zapier or Activepieces integration?

Give your customers another way to use your product.

Get started