Skip to content

Documentation

Requirement

Guidance

  • Use industry tools for your language and technical stack to automate the generation and upkeep of your OAS.

CODE VA

Your API must be added to the CODE VA catalog, VA's Catalog of Developer Essentials. This aids others within VA to be able to discover your API. This is helpful even if your API was written specifically for just one consumer. Documenting the intent of use for your API will make others within VA aware of your API’s existing capabilities.

OpenAPI specification

OpenAPI Specification (OAS) is the predominant API documentation industry standard for describing HTTP APIs. This standard allows people to understand how the API works, generate client code, and what to expect when using the API. All teams at VA must document their APIs using the OpenAPI Specification version 3.0.x.

Documentation for humans and computers

An OAS serves as documentation for human consumers to read when evaluating or using an API, and for machines to use when monitoring and verifying consistency between the API’s specification and its behavior.

OAS documents must pass validation in tools such as the Swagger Editor.

Access to your API's documentation is best when available from a URL that your API hosts. This allows machines and humans to consume this information. For example, if your service is called "rx" then, https://{hostname}/rx/{version}/openapi.json would be a good location to host your API documentation. Using openapi.json or openapi.yaml follows the Open API Specification recommendation.

It is highly recommended to use tools such as Springdoc, or Express OAS Generator to simplify the creation and maintenance of the OAS. You are free to use any tool that works best for your code, language, and situation.

See the guidance under Production Management for keeping the documentation in sync with the API releases.

Example OAS document

Below is an example OAS doc for a fictitious 'Rx' API. Click on the circular buttons labeled with a '+' to view code annotations.

---
openapi: 3.0.1 # (1)
info: # (15)
  title: Rx
  description: An example 'Rx' API that follows the VA API Standards.
  contact:
    name: support@va.gov #(20)
  version: 0.0.0 #(2) 
servers: # (3)
  - url: https://dev-api.va.gov/services/rx/{version}
    description: Development environment
    variables:
      version:
        default: v0
  - url: https://staging-api.va.gov/services/rx/{version}
    description: Staging environment
    variables:
      version:
        default: v0
  - url: https://sandbox-api.va.gov/services/rx/{version}
    description: Sandbox environment
    variables:
      version:
        default: v0
  - url: https://api.va.gov/services/rx/{version}
    description: Production environment
    variables:
      version:
        default: v0
paths:
  /pharmacies:
    get:
      tags:
        - pharmacy
      summary: Returns a list of VA facilities with pharmacies. # (16)
      description: Returns a paginated list of all VA facilities that provide
        pharmacological services.
      operationId: getPharmacies
      responses:
        "200":
          description: Returns a paginated list of VA facilities with pharmacies, and an empty list if none are found for the given criteria.
          content:
            application/json:
              schema: # (4)
                $ref: "#/components/schemas/PharmacyList"
        "500": # (5)
          $ref: "#/components/responses/ErrorInternalServerError"
        "502": # (6)
          $ref: "#/components/responses/ErrorInternalServerError"
        "503": # (7)
          $ref: "#/components/responses/ErrorServiceUnavailable"
      security: # (8)
        - {}
  /prescriptions:
    get:
      tags:
        - prescription
      summary: Returns a list of a Veteran's prescriptions
      description: Given a Veteran's ICN, return a list of their
        prescriptions.
      operationId: getPrescriptions
      parameters: # (17)
        - in: query
          name: icn
          description: MPI ICN
          required: true
          schema:
            maxLength: 17
            minLength: 17
            pattern: ^\d{10}V\d{6}$
            type: string
            example: [1012667145V762142]
      responses:
        "200":
          description: Return a list of the Veteran's prescriptions and an empty list if none are found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PrescriptionList"
        "401": # (18)
          $ref: "#/components/responses/ErrorUnauthorized"
        "403":
          $ref: "#/components/responses/ErrorForbidden"
        "404":
          $ref: "#/components/responses/ErrorNotFound"
        "422":
          description: Unprocessable Entity
          content:
            application/json:
              schema:
                type: object
                example:
                  - errors:
                      - status: 422
                        title: Unprocessable Entity
                        detail: Entity given was unprocessable.
        "500":
          $ref: "#/components/responses/ErrorInternalServerError"
        "502":
          $ref: "#/components/responses/ErrorBadGateway"
        "503":
          $ref: "#/components/responses/ErrorServiceUnavailable"
      security: # (12)
        - production:
            - prescription.read # (19)
components: # (9)
  securitySchemes:
    production:
      type: oauth2
      description: This API uses OAuth2 with the authorization code grant flow.
      flows:
        authorizationCode:
          authorizationUrl: https://api.va.gov/oauth2/authorization
          tokenUrl: https://api.va.gov/oauth2/token
          scopes:
            prescription.read: Retrieve prescription data
    sandbox:
      type: oauth2
      description: This API uses OAuth 2 with the authorization code grant flow.
      flows:
        authorizationCode:
          authorizationUrl: https://sandbox-api.va.gov/oauth2/authorization
          tokenUrl: https://sandbox-api.va.gov/oauth2/token
          scopes:
            prescription.read: Retrieve prescription data
  responses: # (10)
    ErrorUnauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            type: object
            example:
              - errors:
                  - status: 401
                    title: Unauthorized
                    detail: Invalid credentials. The access token has expired.
    ErrorForbidden:
      description: Forbidden
      content:
        application/json:
          schema:
            type: object
            example:
              - errors:
                  - status: 403
                    title: Forbidden
                    detail: You do not have access to the requested resource.
    ErrorNotFound:
      description: Not Found
      content:
        application/json:
          schema:
            type: object
            example:
              - errors:
                  - status: 404
                    title: Not Found
                    detail: The requested resource could not be found.
    ErrorInternalServerError:
      description: Internal Server Error
      content:
        application/json:
          schema:
            type: object
            example:
              - errors:
                  - status: 500
                    title: Internal Server Error
                    detail: An internal API error occurred.
    ErrorBadGateway:
      description: Bad Gateway
      content:
        application/json:
          schema:
            type: object
            example:
              - errors:
                  - status: 502
                    title: Bad Gateway
                    detail: An upstream service the API depends on returned an error.
    ErrorServiceUnavailable:
      description: Service Unavailable
      content:
        application/json:
          schema:
            type: object
            example:
              - errors:
                  - status: 503
                    detail: An upstream service is unavailable.
  schemas:
    PharmacyList:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Pharmacy" # (11)
    Pharmacy:
      type: object
      required:
        - type
        - id
        - attributes
      properties:
        type:
          type: string
          example: ["Pharmacy"]
        id:
          type: string
          example: ["6e976911-2707-4018-a6db-0d1342326379"]
        attributes:
          type: object
          required:
            - id
            - name
            - city
            - state
            - cerner
            - clinics
          properties:
            id:
              type: string
              example: ["358"]
            name:
              type: string
              example: ["Cheyenne VA Medical Center Pharmacy"]
            city:
              type: string
              example: ["Cheyenne"]
            state:
              $ref: "#/components/schemas/State"
    PrescriptionList:
      type: object
      required:
        - data
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/Prescription"
    Prescription:
      type: object
      required:
        - type
        - id
        - attributes
      properties:
        type:
          type: string
          example: ["Prescription"]
        id:
          type: string
          example: ["db8a52f0-b3d2-4cc9-bcab-7053d88737d5"]
        attributes:
          type: object
          required:
            - productNumber
            - referenceDrug
            - brandName
            - activeIngredients
            - referenceStandard
            - dosageForm
            - route
          properties:
            productNumber:
              type: string
              example: ["001"]
            referenceDrug:
              type: boolean
              example: [false]
            brandName:
              type: string
              example: ["FAMOTIDINE PRESERVATIVE FREE"]
            activeIngredients:
              type: object
              required:
                - name
                - strength
              properties:
                name:
                  type: string
                  example: ["FAMOTIDINE"]
                strength:
                  type: string
                  example: ["10MG/ML"]
            referenceStandard:
              type: boolean
              example: ["false"]
            dosageForm:
              type: string
              enum: # (13)
                - INJECTABLE
                - TABLET
              example: ["TABLET"]
            route:
              type: string
              enum:
                - INJECTION
                - ORAL
              example: ["ORAL"]
    State: # (14)
      type: string
      enum:
        - AK
        - AL
        - AR
        - AZ
        - CA
        - CO
        - CT
        - DE
        - FL
        - GA
        - HI
        - IA
        - ID
        - IL
        - IN
        - KS
        - KY
        - LA
        - MA
        - MD
        - ME
        - MI
        - MN
        - MO
        - MS
        - MT
        - NC
        - ND
        - NE
        - NH
        - NJ
        - NM
        - NV
        - NY
        - OH
        - OK
        - OR
        - PA
        - RI
        - SC
        - SD
        - TN
        - TX
        - UT
        - VA
        - VT
        - WA
        - WI
        - WV
        - WY
      example: ["WY"]
  1. OpenAPI Spec 3.0.x is the required OAS specification version when writing your API specification.
  2. This version represents the version of the API and should be updated as releases are delivered.
  3. The servers section should reflect the location of your API hosted domain and API base path. All endpoints should then be relative to this URL.

    For example, for servers you might have one for each of the environments such as: dev, staging, sandbox, production. In this example Rx API for the VA, the servers property has been filled out for all 4 environments.

    It is recommended to NOT provide IP addresses here.

  4. Using $ref properties (references) to link to response body schema definitions helps keep your endpoint definitions short.
  5. Errors happen. We require that all APIs let consumers know the error schema your API will return should your API encounter an unexpected error.
  6. Many VA APIs rely on one or more upstream services for their data. If your API could throw a 500 if an upstream service returns an error, consider returning a 502 error instead, so you and the consumer know a dependency rather than the API is causing the issue.
  7. If your API could throw a 500 if an upstream service is down, consider returning a 503 error instead, so both you and the consumer know a dependency rather than the API is causing the issue.
  8. All endpoints must have a security property, if one is not set globally for all endpoints. In this contrived example, the endpoint does not require authentication. To handle this, the security tag is added with an empty object. This informs the consumer of the endpoint that security is optional.
  9. Often, multiple API operations have some common parameters or return the same response structure. To avoid code duplication, you can place the common definitions in the global components section and reference them using $ref.
  10. Responses, and specifically errors, should be the same layout or shape for all endpoints. Defining them within the components/responses can help reduce the size of your path definitions.
  11. $refs can have $refs. Think of schemas like their database counterparts. Each model and its relations should be a distinct schema.
  12. This endpoint returns a Veteran's prescription history, which qualifies as Personal Health Information (PHI). All endpoints that return PII or PHI must use OAuth.
  13. Enums should be considered constants. As in 'C' style languages, they should be UPPER_CASE with underscores for spaces.
  14. Components don't have to be resources. Any data that appears in multiple locations, such as a list of states, can be a component.
  15. Info tags must include a title and a description. The contact property should not contain an individual's personal or work email address, instead use a generic contact if available.
  16. Path methods must contain a shorter summary and a longer description that explains the purpose and function of each operation.
  17. Requests should use URL or body parameters rather than headers to pass along requisite data unique to that endpoint.
  18. Protected endpoints must define 401 Unauthorized and 403 Forbidden responses.
  19. The prescription.read scope is required for this endpoint so it is listed in the security section.
  20. Contact should be documented within the OAS, and this should be a group email list, Microsoft Teams, or Slack channel where a consumer can send their questions. This must be an actively monitored group email or channel.