## Context and Reference

This design document provides guidelines for specifying API documentation in Go Code using go-swagger, OpenAPI 3.0, and OpenAPI Generator. This document standardizes Ory's V1 API contract.

Ory has an established API and SDK generation system consisting of four parts:

1. Extraction of code comments from Go Code using [Go Swagger](https://goswagger.io/go-swagger/generate-spec/) ( [example](https://github.com/ory/kratos/blob/bd4af9ab9f872b5dacf6e7abaf2cad5ffc83ddd6/Makefile#L89-L93));  
2. Conversion of Swagger 2.0 to OpenAPI Spec 3.0 and applying JsonPatch documents to improve the OpenAPI 3.0 file ( [example](https://github.com/ory/kratos/blob/bd4af9ab9f872b5dacf6e7abaf2cad5ffc83ddd6/Makefile#L96-L109));  
3. Generation of code in the repository itself ( [example](https://github.com/ory/kratos/blob/bd4af9ab9f872b5dacf6e7abaf2cad5ffc83ddd6/Makefile#L113-L120));  
4. Pushing the code to [ory/sdk](https://github.com/ory/sdk) ( [example](https://github.com/ory/kratos/blob/bd4af9ab9f872b5dacf6e7abaf2cad5ffc83ddd6/.github/workflows/ci.yaml#L221-L234)) where it is published to Maven, Packagist, npm, and other package repositories.

As projects grew organically, there is a lack of consistency across the API definitions. This document defines rules and regulations which will bring consistency to our APIs.

## Goals and Non-Goals

**Goals:**
- Provide consistent naming patterns for API methods.
- Solve coarse-grained versioning.
- Use the existing tool-chain.

**Non-goals:**
- Fine-grained versioning will be discussed in future work.
- Fixing limitations of OpenAPI Generator:
  - Has often problems with discriminators;
  - Some templates have code generation bugs;
  - Most of the times all code is dumped into one directory making naming conflicts likely;
  - Fixing limitations of Go Swagger:
    - Only generates Swagger 2.0 (OpenAPI 2.0);
    - Has wonky enum generation;
    - Discriminators do not work;
    - Has problems when multiple definitions for models / parameters / resources exist;
    - We need to explicitly include packages we want to use for spec generation, otherwise random definitions end up in the spec;
    - gRPC API design is left to future work.

## The Design for OpenAPI

This section discusses how Ory uses OpenAPI 3.0.

### Routes

Routes are the functions of an RPC infrastructure and are annotated using [`swagger:route`](https://goswagger.io/go-swagger/reference/annotations/route/):

```go
// swagger:route [method] [path pattern] [?tag1 tag2 tag3] [operation id]
```

#### Naming

All route operator names should be named using the following schema:
- `get{ResourceName}` (e.g. `getIdentity`): Retrieves a resource using `GET`.
- `set{ResourceName}` (e.g. `setIdentity`): Replaces a resource using `PUT`.
- `patch{ResourceName}` (e.g. `patchIdentity`): Patches a resource using `PATCH`.
- `delete{ResourceName}` (e.g. `deleteIdentity`): Deletes a resource using `DELETE`.
- `list{ResourceName}s` (e.g. `listIdentities`): Retrieves a list of resources with pagination and optionally filtering using `GET`.

If the schema above does not fit your use case, please discuss it with a code owner.

##### Parameters

Parameters of requests or responses should always be written in `snake_case`.

This includes:
- Query (sometimes referred to as "search") parameters (e.g. `?query_param=1&query_param2=test`)
- Parameters in a request or response body (form values or JSON)
- Header names

```go
// Update Registration Flow with Password Method
// swagger:model updateRegistrationFlowWithPasswordMethod
type UpdateRegistrationFlowWithPasswordMethod struct {
    // The CSRF Token
    CSRFToken string `json:"csrf_token"`
    // ... other fields
}
```

#### Tags

Tags should denote a functional domain:
- identity
- oauth2
- project
- relationships

#### Security

Unless specified otherwise, administrative actions require an Ory Access Token to be included:

```go
// swagger:route GET /admin/identities identities listIdentities
// ...
// Security:
//   oryAccessToken:
// ...
func listIdentities(w http.ResponseWriter, r *http.Request) {}
```

Public routes that don't need authorization can omit this parameter.

#### Requests

Routes which have request parameters should have the name of the operation. If the request has a body either use an existing model, or create a struct named like the parameter and postfixed with `RequestBody`.

## API

This section iterates over samples of existing projects' OpenAPI Specs to clarify naming conventions.

### Exemplary Definitions

| Method | Notes (Aeneas) | Route | Tag | Op | Request | Response |
| --- | --- | --- | --- | --- | --- | --- |
| GET |  | /.well-known/openid-configuration | oidc | discoverOpenIdConnectConfiguration |  | oidcConfiguration, errorOAuth2 |
| GET |  | /.well-known/jwks.json | wellknown | discoverWellKnownJsonWebKeys |  | jsonWebKeySet, errorOAuth2 |
| GET |  | /admin/clients | oauth2 | listOAuth2Clients | listOAuth2Clients | listOAuth2Clients, errorGeneric |
| POST |  | /admin/clients | oauth2 | createOAuth2Client | createOAuth2Client | oauth2Client, errorOAuth2ClientNotFound, errorGeneric |
| PUT |  | /admin/clients/{id}/lifespans | oauth2 | setOAuth2ClientLifespans | setOAuth2ClientLifespans | oauth2Client, errorOAuth2ClientNotFound, errorGeneric |
| DELETE |  | /admin/keys/{set} | jwk | deleteJsonWebKeySet | deleteJsonWebKeySet | empty, errorJsonWebKeySetNotFound |
| DELETE |  | /admin/keys/{set}/{kid} | jwk | deleteJsonWebKey | deleteJsonWebKey | empty, errorJsonWebKeyNotFound |
| PUT |  | /admin/oauth2/auth/requests/consent/accept | oauth2 | acceptOAuth2ConsentRequest | acceptOAuth2Consent | acceptedOAuth2ConsentRequest, errorGeneric |
| DELETE |  | /admin/oauth2/auth/sessions/consent | oauth2 | revokeOAuth2ConsentSessions | revokeOAuth2ConsentSessions | empty, errorGeneric |
| POST |  | /admin/trust/grants/jwt-bearer/issuers | oauth2 | trustOAuth2JwtGrantIssuer | trustOAuth2JwtGrantIssuer | trustedOAuth2JwtGrantIssuer, errorGeneric |
| GET |  | /admin/oauth2/introspect | oauth2 | introspectOAuth2Token | introspectOAuth2Token | introspectedOAuth2Token, errorOAuth2 |
| POST |  | /oauth2/register | oidc | createOidcDynamicClient | oAuth2Client | oAuth2Client, errorOAuth2 |
| GET |  | /oauth2/register/{id} | oidc | getOidcDynamicClient | oAuth2Client | oAuth2Client, errorOAuth2 |
| POST |  | /oauth2/revoke | oauth2 | revokeOAuth2Token | revokeOAuth2Token | emptyResponse, errorOAuth2 |
| GET |  | /oauth2/sessions/logout | oidc | revokeOidcSession | revokeOidcSession | emptyResponse, 303 |
| POST |  | /oauth2/token | oauth2 | oauth2TokenExchange | oauth2TokenExchange | oauth2Tokens, errorOAuth2 |
| GET |  | /oauth2/auth | oauth2 | oauth2Authorize | oauth2Authorize | 302, html |
| GET |  | /userinfo | oidc | oidcUserinfo | oidcUserinfo | oidcUserinfo, errorOAuth2 |
| POST |  | /relation-tuples | relationship | createRelationship | createRelationship | relationship, errorGeneric |
| GET |  | /relation-tuples/check/openapi | relationship | hasRelationship | hasRelationship | hasRelationship, errorGeneric |
| GET |  | /relation-tuples/check | relationship | hasRelationshipOrError | hasRelationshipOrError | hasRelationship, errorNoRelationship, errorGeneric |
| GET |  | /relation-tuples/expand | relationship | expandRelationship | expandRelationship | expandedRelationship, errorGeneric |
| GET |  | /.well-known/ory/webauthn.js | identity | getWebAuthnJavaScript |  | 200 application/javascript |
| GET |  | /admin/courier/messages | courier | listCourierMessages | listCourierMessages | listCourierMessages, errorGeneric |
| GET |  | /admin/identities/{id} | identity | getIdentity | getIdentity | identity, errorIdentityNotFound, errorGeneric |
| DELETE |  | /admin/identities/{id}/sessions | identity | deleteIdentitySessions | deleteIdentitySessions | empty, errorIdentityNotFound, errorGeneric |
| POST |  | /admin/recovery/code | identity | createRecoveryCodeForIdentity | createRecoveryCodeForIdentity | identityRecoveryCode, errorGeneric |
| POST |  | /admin/sessions/{id}/extend | identity | prolongIdentitySession | prolongIdentitySession | session, errorGeneric |
| GET |  | /schemas/{id} | identity | getIdentitySchema | getIdentitySchema | identitySchema, errorGeneric |
| POST |  | /self-service/login | fontend | submitFrontendLogin | submitFrontendLogin | 302, frontendLoginResult |
| GET |  | /self-service/login/api | fontend | initFrontendLoginWithoutBrowser | initFrontendLoginWithoutBrowser | 302, frontendLogin, errorGeneric |
| GET |  | /self-service/login/browser | fontend | initFrontendLoginForBrowser | initFrontendLoginForBrowser | frontendLogin, errorGeneric |
| GET |  | /sessions | identity | getMySessions | getMySessions | getMySessions, errorGeneric |
| GET |  | /sessions/whoami | identity | toSession | toSession | session, errorGeneric, errorUnauthorized, errorForbidden |
| GET |  | /.well-known/identity-meta-schema | identity | getIdentityMetaSchema |  | 200 application/json, errorGeneric |
| DELETE |  | /cname/{cname_host} | project | deleteCustomDomain | deleteCustomDomain | empty, errorGeneric |
| GET |  | /identity-schemas | project | getManagedIdentitySchemas |  | getManagedIdentitySchemas, errorGeneric |
| GET |  | /invites/manage/{project}/project | project | getProjectInvites | getProjectInvites | getProjectInvites, errorGeneric |
| GET |  | /projects/{project_id}/cname | project | listProjectCustomDomains | listProjectCustomDomains | listProjectCustomDomains, errorGeneric |
| PATCH |  | /projects/{project_id}/revision/{revision_id} | project | patchProjectUsingRevision | patchProjectUsingRevision | patchProjectUsingRevision, errorGeneric |
