Voucherify builds and maintains REST API documentation and SDKs to make it easier for software developers to understand and integrate Voucherify into their e-commerce platforms.
This document describes all deliverables and their development process.
The Guides and API Reference pages are hosted on readme.io, which is a platform for creating and hosting developer documentation. However, the source of the documentation content is stored in the Voucherify Open API GitHub repository.
The guides are stored purely as Markdown files in the guides folder. They can be uploaded to the readme.io platform via readme CLI.
The API Reference pages are built by readme.io. The platform combines the OpenAPI file that describes Voucherify API endpoints, parameters, and responses with the Markdown files from the reference folder.
API Endpoint Pages like GET voucher describe a REST API endpoint, including details like path, HTTP method, path parameters, body parameters, response schema, and response statuses. On the right side of those pages, there is a Playground Widget that developers can use to make test API calls. Readme.io builds those pages using information about the REST API from the uploaded OpenAPI file and displays UI, so users can explore all the details.
For each endpoint page, there is a corresponding dummy Markdown file like VOUCHERS-Get-Voucher.md. This page allows editing the appearance of the page displayed in readme.io, in particular:
- The Markdown attributes section at the beginning of the file wrapped by
---describes the page title, type, slug, order, and visibility. [block:html]section that adds custom styles to the page that hides unnecessary UI elements like Playground language selector or expandable readme object exploration widget. It can be also used to display the "Beta" tag next to the title.
Reamde.io platform compares the operationId endpoint details attribute from the OpenAPI file with the slug from Markdown attributes to combine them and display the final version of the API endpoint page.
All API endpoint pages are grouped by sections like Vouchers, Campaigns, or Promotions. Those sections are built by readme.io based on the tags endpoint details attribute from the OpenAPI file. The attribute must be used in the parentDocSlug attribute of the dummy Markdown file.
Data model pages like Voucher Object describe the schema of the main building blocks used in specific sections. There are two types of data model pages:
- Using readme.io expandable object exploration widget, like on the Validation Object page,
- Displaying the schema of the object with all attributes in a table like on the Voucher Object page.
We believe that presenting object details in a table is more intuitive for developers. Unfortunately, readme.io does not have the feature to display building block objects defined in OpenAPI in a table format, so we have built a custom JS script (build-update-md-tables-from-openapi). The script generates Markdown tables automatically from the OpenAPI file and puts them into Markdown files in the reference-docs folder, e.g.: https://github.com/voucherifyio/voucherify-openapi/blob/master/docs/reference-docs/VOUCHERS-Voucher-Object.md. Once the Markdown files are created, they are uploaded to readme.io with the readme.io CLI.
Pages from the introduction section, like Introduction – What is Voucherify API?, are Markdown pages uploaded to readme.io with readme.io CLI. Their content can be found along with other Markdown files inside the docs/reference-docs.
To label an API endpoint as a Beta version in readme.io, add the following details in its Markdown file:
- Add
[Beta]postfix to the page title (titlemarkdown attribute) - Add the following style to the
[block:html]section within the<style></style>tags:
h1::after {\n content: \"BETA\";\n background-color: rgb(237, 117, 71);\n color: rgb(255, 255, 255);\n border-radius: 2rem;padding: 8px 13px 8px;\n white-space: nowrap;font-size:12px;\n}Note that OpenAPI files slightly differ depending on where we use them.
- [production/readOnly-openAPI.json] - specification version 3.0.1 for all external viewers.
- [reference/OpenAPI.json] - Specification version 3.0.1 with
"type": "null"usages. - [tmp/referenceToUpload/OpenAPI.json] - Used for readme.io specification version 3.0.1, but it is marked as 3.1.0 to skip the validation check by readme.io. It uses
"type": "null". - [tmp/reference/{language}/OpenAPI.json] - Used to generate an SDK.
If you want to contribute to the OpenAPI file, you MUST do it in the reference/OpenAPI.json file, because all other OpenAPI files are generated from this file!
To update the [production/readOnly-openAPI.json] file, run the npm run build-production-openapi
The [tmp/referenceToUpload/OpenAPI.json] file is generated while running the npm run create-clean-project -- (parameters) command.
The [tmp/reference/{language}/OpenAPI.json] files are generated while running npm run prepare-open-api -- --language=(language) command. The available languages are ruby and python.
The OpenAPI Specification (OAS) is used to create the Voucherify API documentation. The Voucherify OpenAPI file is located in the Voucherify OpenAPI GitHub repository.
We use Stoplight to edit the OpenAPI file as it gives a readable UI that helps to edit the very large json file. Everyone can create a free account on the Stoplight platform.
How to edit the OpenAPI file:
- Upload
./reference/OpenAPI.jsonfile to the Stoplight platform. - Make changes to the OpenAPI.json file with the Stoplight UI.
- Export the modified OpenAPI content and update the OpenAPI.json file in the repository.
- Ensure that the OpenAPI.json file has only expected changes.
[!WARNING] Each OpenAPI change should be tested by reviewing the documentation on readme.io after the full documentation update process.
The documentation of the events that are used in Voucherify webhooks is generated from an OpenAPIWebhooks.json file.
If you want to contribute to this documentation, follow the guidelines for the Voucherify OpenAPI documentation.
Tips:
- To add a new event, add it to the
"paths"resources in the OpenAPIWebhooks.json file. Events use thePOSTmethod. - To add a new event category, add an object to the
"tags"section. Specify the name and description. The name and the description should be the same, starting with theEventsword. - If you want to add a page to the Events section, add a Markdown file to the
docs/custom-webhook-sitesfolder.- Note: these files require a header wrapped with
---to describe the page title, type, slug, order, and visibility.
- Note: these files require a header wrapped with
- If an event requires an additional description, add a Markdown file to the
docs/webhook-introductionsfolder. The file should be named in the following format:events-{event category tag name}-{event > post > summary > value}. Example:events-voucher-enabled.md.- Note: these files do not require a header.
When building new models, follow the following name convention:
- Use the PascalCase.
- If a model is used as a specific API endpoint description (0-level model), follow the pattern:
{Client?}{PathNameResult}{Action}{Differentiator?}{Request|Response}{Body|Query}, where:- (optional)
Client: Use for all client schemas. PathNameResult:location.pathnameWITHOUTv1andpath parameterswritten in PascalCase. Examples:/v1/rewards/{rewardId}/assignments=>RewardsAssignments/v1/rewards/{rewardId}/assignments/{assignmentId}=>RewardsAssignments/v1/rewards/{rewardId}/assignments/{assignmentId}/redemptions=>RewardsAssignmentsRedemptions/client/v1/rewards/{rewardId}/assignments/{assignmentId}/redemptions=>ClientRewardsAssignmentsRedemptions
Action: Either taken from an HTTP method, e.g.List,Get,Update,Delete,Createor what the endpoint does, e.g.Track,Validate,Import,ExportGet(single record),List(multiple record)Update(single record),UpdateInBulk(multiple record),Delete(single record),Create(single record),CreateInBulk(multiple record)
- (optional)
Differentiator: Sub-model title when a 0-level model contains onlyoneOf. The title of those models must be like the schema name but inTitle Caseand the description must follow the pattern:{Response/Request} {Body/Query} schema for **{Method}** {Path} {OPTIONALLY: and **{Method}** {Path}}. Examples:Base [PublicationsCreateBaseResponseBody](common part of other child models)Vouchers [PublicationsCreateVouchersResponseBody]Voucher [PublicationsCreateVoucherResponseBody]
RequestorResponseBodyorQuery
- (optional)
Example of a model that needs a Differentiator:
"PublicationsCreateResponseBody": {
"title": "Publications Create Response Body",
"type": "object",
"description": "Response body schema for **POST** `v1/publication` and **GET** `v1/publications/create`.",
"oneOf": [
{
"$ref": "#/components/schemas/PublicationsCreateVoucherResponseBody"
},
{
"$ref": "#/components/schemas/PublicationsCreateVouchersResponseBody"
}
]
},- If a model is used by more than one API endpoint (general model), use simple domain language, e.g.
Customer,Category,Discount,DiscountUnit. - If a part of a model is used by more than one schema, save this part under a new schema and use it with an
allOfoperator.
If you see a schema with a wrong name, don't hesitate to correct it!
type- should beobjectorarraymostly but in some cases, it could be optional.title- should be the same as the name of the model.description- should point to the API endpoint that uses this model e.g.{type} body schema for **{method}** {path}.properties / oneOf / allOf- should contain all attributes that are used in the API endpoint or$refto another schema.
{
"RedemptionsGetResponseBody": {
"type": "object",
"title": "Redemptions Get Response Body",
"description": "Response body schema for **GET** `v1/redemptions/{redemptionId}`",
"oneOf": [
{
"$ref": "#/components/schemas/Redemption"
},
{
"$ref": "#/components/schemas/RedemptionRollback"
}
]
}
}For example:
- The general voucher model, used in many different API endpoints, should have the name
Voucher(currently, it has a name:Voucher) - for path
GET /v1/vouchers(list vouchers), we have a1_res_vouchers_GET0-level model that should be namedVouchersListResponseBody. - for path
GET /v1/vouchers(list vouchers), we have a1_res_vouchers_GET0-level model which has a sub-modelVoucher_list_vouchersthat should be namedVouchersListResponseBody. - General model
Voucheris used in many paths (GET /v1/vouchers/{code},POST /v1/vouchers/qualification,GET /v1/publications/create); therefore, it should be renamed toVoucher.
[!NOTE] Most likely, the general model will be the same as used in the GET method. For example,
CategoriesGetResponseBodyis equal by reference toCategory. This model most likely will not be used inPUTrequests, because the response in aPUTrequest always returns value inupdated_at, so you will need to create a duplicated model just for the update response.
Contribute with the following good practices in mind:
- For literal unions, use
enum, - For type unions, use
oneOf, - For attributes that may contain
null, add"nullable": true, - If an attribute is always
null, set"type": "null", - For dates, use
"type": "string", "format": "date-time"or"type": "string", "format": "date", - For the object type
object, add therequiredattribute which should contain a list of required attributes in the object, - A
nullablecannot be next to a$ref. Runnpm run fix-schemas-with-refsto fix it. writeOnlyandreadOnlyflags should not be used, because they cause errors in generating SDKs