API Contract & Documentation
Rules for API-first design, OpenAPI quality, and publishable documentation.
These rules explain how to specify, document, and publish APIs. The goal is clear, consistent contracts and docs that customers and partners can actually use.
API Design & Documentation
#100 - Follow API-First
Intent: start with the contract so everyone builds against the same, reviewable API.
Define and review the API specification before implementation hardens.
How to implement:
- Draft OpenAPI early (paths, schemas, errors, auth, pagination, filtering).
- Review the design with stakeholders (API owners, SDK/tooling owners, and at least one consumer) before writing production code.
- Keep spec and code in lockstep in version control; treat the spec as a first-class artifact.
- Prefer contract tests (or spec validation) in CI so changes are visible and reviewable.
Checklist:
- OpenAPI exists for the service and is in the same repo as the implementation.
- Every operation has: summary/description,
operationId, tags, request/response schemas, error responses, and security requirements. - Examples are present for key request/response bodies.
- Backward-compatibility is evaluated for any change within the same major version.
Testability:
- CI runs OpenAPI validation and fails on invalid specs.
- Breaking changes are detected in review (diffs + compatibility checks) before release.
#101 - Provide an OpenAPI Specification
Intent: make the contract easy to find, review, and use for tools/SDKs.
Provide an OpenAPI Specification (OAS) for every API and keep it current.
Requirements:
- The OpenAPI document MUST be stored in version control alongside the service code.
- The OpenAPI document MUST be published for consumers (docs site, artifact registry, or served by the service).
- The OpenAPI document MUST be validated in CI (linting/validation) so invalid specs cannot be released.
Recommended:
- Use a changelog or release notes that reference spec changes.
- Keep examples accurate and safe (no secrets or real customer data).
Testability:
- CI fails on invalid OpenAPI syntax.
- CI or review checks enforce required per-operation metadata (tags,
operationId, security, responses).
#102 - Provide an API User Manual
Intent: make the API usable without insider knowledge.
Provide an API user manual that complements OpenAPI.
What to include:
- Getting started: authentication approach, base URLs/environments, common headers.
- Core workflows: step-by-step guides per API slice / use case (create, update, search, pagination).
- Edge cases: validation rules, idempotency expectations, conflict handling, eventual consistency.
- Error handling: common errors and recommended client behavior (retry vs no-retry).
- Performance: pagination guidance, filtering/sorting limitations, rate limits if applicable.
OpenAPI linkage (recommended):
- Link the manual using OpenAPI
externalDocs(root-level and/or per-tag / per-operation where appropriate).
Testability:
- Documentation exists and is reachable from the published OpenAPI (via
externalDocs). - At least one end-to-end example per primary workflow is present and matches schemas.
#113 - Follow SailPoint API Guidelines
Intent: keep the API experience consistent across teams and products.
APIs MUST follow these guidelines.
How this is enforced:
- Treat the OpenAPI spec as the contract and review guideline compliance as part of API review.
- Use automated checks where possible (linting, OpenAPI validation, compatibility checks, contract tests).
Exceptions:
- Any deliberate exception MUST be documented with rationale (and a remediation plan where applicable) using the review ledger.
Testability:
- Use the Rules JSON (
/rules.json) for deep links and tooling references.
#114 - Provide a Detailed API Description
Intent: make the API understandable without insider context.
APIs MUST provide a detailed description that covers what the API does and how consumers should use it.
Minimum content:
- Purpose and scope (what is in-scope vs out-of-scope).
- Intended audience (internal vs external) and required licensing/entitlements (see [#115]).
- Authentication model and high-level authorization expectations (see [#300], [#206]).
- Primary workflows and “happy path” usage (link to guides/user manual where applicable; see [#102]).
- Major edge cases and semantics that affect client correctness:
- pagination defaults
- filtering/sorting constraints
- idempotency/retry guidance
- consistency expectations
- Versioning/deprecation model and how changes are communicated (see [#208], [#209]).
Where to put it:
- In OpenAPI
info.descriptionand/or tag-level descriptions. - Link to deeper guides via OpenAPI
externalDocs.
Testability:
- A reviewer can answer: what is this API for, who can use it, and how do I perform the primary workflows?
#115 - Describe Every Parameter and Property
Intent: remove ambiguity so clients can implement without guesswork.
Every query parameter, path parameter, request property, and response property MUST have a clear description.
Descriptions MUST cover (as applicable):
- Meaning/semantics (what it represents, not just its data type).
- Constraints: allowed values, ranges, patterns, maximum lengths.
- Default behavior when omitted (see [#709]).
- Null/omission behavior where relevant (see [#700]).
- Any security/privacy implications (e.g., whether values are tenant-scoped, PII).
Anti-patterns:
- Empty descriptions.
- Descriptions that restate the property name (e.g., “The status.”).
Testability:
- Reviewers can read the spec and understand how to populate requests and interpret responses without external tribal knowledge.
#116 - Provide Examples for Every Parameter and Property
Intent: make docs copy/pasteable and reduce mistakes.
Every query parameter, path parameter, request property, and response property MUST include at least one accurate example.
Requirements:
- Examples MUST conform to the declared schema and formats.
- Prefer OpenAPI
example/examplesso tooling can render them. - Provide examples for both success and error responses (see [#404]).
- Never include secrets or real customer data.
Testability:
- Examples are validated during review (or via automated checks) to match schemas.
#117 - Keep Operation Summaries to 5 Words or Fewer
Intent: keep generated docs easy to scan.
Operation summary fields SHOULD be concise (prefer five words or fewer).
Guidance:
- Put details, caveats, and semantics in
description. - Prefer action-oriented summaries aligned with the HTTP method (e.g., “List accounts”, “Get account”, “Create account”).
Examples:
- ✅
List accounts - ✅
Get account - ❌
Get accounts for a given identity profile and include nested entitlements(too long; belongs indescription)
Testability:
- Reviewers can spot-check that summaries are short and that details live in
description.
#120 - Describe the Filters Parameter (Standard Format)
Intent: keep filter docs consistent so users can reuse knowledge across endpoints.
When an endpoint supports the filters parameter, APIs MUST use the prescribed description template below. Customize the supported-field list per endpoint.
Template (copy/paste into OpenAPI description):
Filters results using the standard expression language.
Syntax:
filters=<expr>
Operators:
eq, ne, lt, lte, gt, gte, co (contains), sw (startsWith), ew (endsWith),
in (set membership), pr (present)
Grouping:
Use parentheses for grouping and AND/OR for boolean composition.
Supported fields:
- fieldA (string)
- fieldB (enum: ...)
- created (date-time)
Examples:
filters=status eq \"ACTIVE\"
filters=(status eq \"ACTIVE\" or status eq \"PENDING\") and createdAt gt \"2025-01-01T00:00:00Z\"Testability:
- Ensure each endpoint documents the supported field whitelist and includes at least one filter example.
Related: shared query story in [#604] and implicit/default behavior guidance in [#603].
#121 - Describe the Sorters Parameter (Standard Format)
Intent: keep sorting docs consistent so users can reuse knowledge across endpoints.
When an endpoint supports the sorters parameter, APIs MUST use the prescribed description template below. Customize the supported-field list per endpoint.
Template (copy/paste into OpenAPI description):
Sorts results by one or more fields.
Syntax:
sorters=<field>:<direction>[,<field>:<direction>...]
Direction:
asc | desc
Supported fields:
- name
- createdAt
- status
Default ordering:
If sorters is omitted, results are ordered by <defaultSortPolicy>.
Examples:
sorters=name:asc
sorters=status:asc,created:descTestability:
- Ensure sort fields are documented and the API guarantees a stable ordering for pagination.
Related: shared query story in [#604] and default ordering policy in [#603].
#122 - Provide a camelCase operationId for Every Operation
Intent: support stable SDKs and tooling.
Every operation MUST define a unique operationId in camelCase.
Stability (anti-churn):
operationIdvalues are part of the public contract for tooling/SDKs.- Do not rename
operationIdvalues without a compatibility plan. - If a rename is unavoidable:
- keep the old
operationIdwhere possible (or maintain an alias in tooling) - document the change in changelogs/release notes
- keep the old
Uniqueness:
operationIdMUST be unique across the entire spec (including across tags).
Recommended patterns:
- GET collection:
list<ResourcePlural> - GET item:
get<Resource> - POST collection:
create<Resource> - PUT/PATCH:
update<Resource> - DELETE:
delete<Resource>
Additional patterns (recommended):
- POST search endpoints (see [#604]):
search<ResourcePlural> - Long-running jobs/resources:
create<Thing>Job(POST)get<Thing>Job(GET)list<Thing>Jobs(GET)
Examples:
| Method | Path | operationId |
|---|---|---|
| GET | /accounts | listAccounts |
| GET | /accounts/{id} | getAccount |
| POST | /accounts | createAccount |
| POST | /accounts/search | searchAccounts |
| PATCH | /accounts/{id} | updateAccount |
| DELETE | /accounts/{id} | deleteAccount |
Testability:
- Spec validation ensures every operation has a unique, camelCase
operationId.
#123 - Provide a Tag for Every Operation
Intent: keep docs organized and easy to navigate.
Each operation MUST be assigned exactly one tag that exists in the root spec’s tags array to enable proper organization and documentation generation.
Guidance:
- Prefer resource-centric tags (e.g.,
Accounts,Entitlements). - Keep tags stable across versions.
- Ensure tag descriptions explain what belongs in the tag (helps new endpoints land in the right place).
Testability:
- Spec validation checks tags exist and operations are tagged consistently.
#124 - Provide x-sailpoint-resource-operation-id for Path Parameters
Intent: let SailPoint tooling map path parameters to the correct operation.
For path parameters (except enums and user-defined values), APIs MUST provide a camelCase operation identifier under x-sailpoint-resource-operation-id.
Where it lives:
- On the path parameter definition in OpenAPI.
Example:
paths:
/accounts/{accountId}:
get:
operationId: getAccount
parameters:
- name: accountId
in: path
required: true
schema:
type: string
x-sailpoint-resource-operation-id: getAccountRules:
- The value MUST be camelCase.
- The value SHOULD align with the operation’s
operationId(see [#122]) so the mapping is stable. - Do not change these identifiers without a compatibility plan (tooling may depend on them).
Testability:
- Reviewers can verify each path parameter includes
x-sailpoint-resource-operation-idwhen required.
Text Formatting & Naming Conventions
#103 - Write in U.S. English
Intent: keep wording consistent by using one English variant.
Use U.S. English spelling and conventions across the API surface and documentation.
Scope:
- OpenAPI descriptions, summaries, tags, and schema documentation.
- Error
title/detailtext and any human-readable messages. - Developer documentation (guides, rule pages).
Notes:
- This rule does not require clients to send U.S. English values unless a field is explicitly documented as English text.
- Prefer consistent terminology across endpoints (avoid synonyms for the same concept).
Checklist:
- U.S. spelling is used consistently (e.g., "authorization" not "authorisation").
- The same terms are used for the same concepts across the API (avoid renaming concepts in prose).
- Acronyms are used consistently and expanded once when first introduced.
Testability:
- Reviewers spot-check new/changed endpoints and descriptions for consistent spelling/terminology.
#104 - Use camelCase for JSON Properties
Intent: keep JSON payloads consistent and predictable across teams and SDKs.
JSON property names MUST use camelCase.
Scope:
- Applies to JSON request/response bodies.
- For query parameters and path parameters, use [#207] and [#114].
- ASCII-only identifier requirements are defined in [#105].
Examples:
- ✅
createdAt,identityProfileId,lastLogin - ❌
created_at(snake_case) - ❌
CreatedAt(PascalCase) - ❌
created-at(kebab-case)
Acronyms:
- Prefer
oauthClientIdoverOAuthClientID. - Be consistent across the entire API surface.
#105 - Use ASCII for API Identifiers
Intent: maximize interoperability across languages, tooling, URLs, and SDKs.
All API identifiers MUST use ASCII characters only.
Applies to:
- URL path segments (resource names)
- Path parameter names (
{identityId}) - Query parameter names (
?sortOrder=asc) - JSON property names (
createdAt) - Enum tokens when they represent identifiers (
"PENDING_APPROVAL") - Header names in documentation/examples
Does not apply to:
- Free-form human text fields (e.g.,
displayName,description) where UTF-8 is expected and documented.
Examples:
- ✅
identityProfileId - ✅
createdAt - ❌
créatedAt - ❌
名前
#106 - Pluralize Array Property Names
Intent: reduce ambiguity so payloads explain themselves.
Array properties SHOULD use plural names to signal multiple values.
Examples:
- ✅
items,accounts,entitlements,roles - ✅
children(already plural) - ✅
data/metadata(acceptable common exceptions; use consistently) - ❌
itemfor an array - ❌
accountfor an array
Notes:
- If a plural name is awkward or ambiguous, choose a clear collective noun and document it.
- Be consistent across endpoints (don’t call it
itemsin one response andresultsin another unless there’s a meaning difference).
#107 - Use Lowercase Hyphenated Path Segments and camelCase Path Parameters
Intent: make URLs predictable, readable, and consistent across APIs.
Use lowercase with hyphens for URL path segments (e.g., /user-profiles) and camelCase for path parameters (e.g., {userId}).
Examples:
- ✅
/identity-profiles/{identityProfileId} - ✅
/access-requests/{accessRequestId} - ❌
/IdentityProfiles/{IdentityProfileId}(wrong casing) - ❌
/identity_profiles/{identity_profile_id}(snake_case)
Testability:
- Reviewers can spot-check paths and parameter names for casing consistency.
#108 - Use camelCase for Query Parameters
Intent: keep the request surface consistent with JSON conventions and reduce cognitive load.
Query parameter names MUST use camelCase.
Examples:
- ✅
?sortOrder=asc&maxResults=50 - ✅
?includeDisabled=true - ✅
?createdAfter=2026-01-01T00:00:00Z
Anti-examples:
- ❌
?sort_order=asc(snake_case) - ❌
?SortOrder=asc(PascalCase) - ❌
?include-disabled=true(kebab-case)
Notes:
- Prefer conventional query parameter names for pagination/filtering/sorting (see [#600]).
- Be consistent across endpoints (don’t use
pageSizein one API andlimitin another).
#109 - Use Upper-Case Words with Hyphens
Intent: improve documentation consistency and readability.
When documenting HTTP header names (including custom headers), prefer the conventional Upper-Case-Words-With-Hyphens style.
Examples:
- ✅
Content-Type,If-Match,If-None-Match,Retry-After - ✅
X-Request-Id(if using anX-prefixed header during a migration) - ❌
content-type - ❌
IF_MATCH
Notes:
- HTTP header field names are case-insensitive at runtime; this is a documentation/style rule.
- Prefer standardized headers over custom ones where possible (see [#400]).
#110 - Pluralize Collection Resource Names
Intent: make endpoints predictable and self-explanatory.
Collection endpoints MUST use plural resource names.
Examples:
- ✅
GET /accounts(collection) - ✅
GET /accounts/{accountId}(single resource) - ✅
GET /identity-profiles/GET /identity-profiles/{identityProfileId}
Anti-examples:
- ❌
GET /account(ambiguous) - ❌
GET /Accounts(wrong casing)
Notes:
- Use domain language and stable nouns (see [#506]).
- If a term is uncountable (rare), pick a consistent convention and document it.
#111 - Follow Naming Convention for Permissions (Scopes)
Intent: make scopes predictable, discoverable, and reusable.
Scopes MUST follow a consistent naming convention so clients can understand and request permissions without per-service guesswork.
Canonical format:
<domain>:<resource>:<action>Conventions:
<domain>: stable product/domain namespace (lowercase, ASCII)<resource>: plural resource family (lowercase, ASCII)<action>: one of the approved action verbs
Approved action verbs (default set):
read: read-only access (GET, and “search semantics” endpoints)write: create/update/delete access (POST/PATCH/PUT/DELETE)admin: elevated/tenant-admin operations (break-glass or privileged operations)
If you need additional verbs, they MUST be standardized for the domain and documented (avoid ad-hoc verbs per endpoint).
Examples:
identity:accounts:readidentity:accounts:writeidentity:accounts:adminidentity:entitlements:read
Endpoint documentation requirement (ties to [#300]/[#206]):
- OpenAPI
securityrequirements MUST list the full set of scopes accepted for the operation. - If an endpoint supports multiple privilege modes, document them explicitly; do not hide scope requirements in prose-only text.
Anti-examples:
Identity.Accounts.Read(wrong separators/case)accounts:read(missing domain)identity:readAccounts(inconsistent action encoding)
Testability:
- A reviewer can infer scope meaning from name alone.
- Scopes referenced in OpenAPI match the convention and appear consistently across endpoints.
#112 - Declare Enum Values in UPPER_SNAKE_CASE Strings
Intent: make enum tokens stable and readable.
Enum values MUST be expressed as UPPER_SNAKE_CASE strings.
Examples:
- ✅
"ACTIVE" - ✅
"PENDING_APPROVAL" - ❌
"active"(wrong case) - ❌
"PendingApproval"(camel/Pascal case) - ❌
"Pending Approval"(spaces)
Compatibility:
- Enum tokens are part of the public contract. Do not rename existing enum values without a compatibility/migration plan.
OpenAPI example:
status:
type: string
enum: [ACTIVE, PENDING_APPROVAL, DISABLED]#118 - Avoid Qualifying Verbs
Intent: keep property names concise and stable.
Boolean fields and other descriptive properties SHOULD avoid qualifying verbs like is, has, can when the adjective alone is clear.
Examples:
- Prefer:
- ✅
enabledoverisEnabled - ✅
activeoverisActive - ✅
visibleoverisVisible
- ✅
- Acceptable exceptions:
- When the adjective is ambiguous without a verb (e.g.,
canDeletemay be clearer thandeletabledepending on domain language).
- When the adjective is ambiguous without a verb (e.g.,
Testability:
- Naming is consistent within a resource and avoids mixed patterns.
#119 - Use Positive Semantics for Boolean Fields
Intent: reduce double-negative confusion in client code.
Boolean properties SHOULD use positive semantics.
Examples:
- Prefer:
- ✅
enabledoverdisabled - ✅
includedoverexcluded - ✅
verifiedoverunverified
- ✅
Legacy guidance:
- If an API already has a negative boolean (
disabled), avoid adding a second, inverted boolean (enabled). Instead, keep one field and document it clearly. - If you must migrate, add a new positively-named field, deprecate the old one, and maintain both during a migration window.
Testability:
- New fields prefer positive semantics; any legacy inversions are documented.