OpenAPI Documentation Settings
Your endpoint definitions do double duty: besides routing requests, they feed the OpenAPI documentation that Scalar serves at /docs. SQL endpoints even get schema discovery for free, with column names and types read from the database at startup. Other endpoint types describe themselves through the Documentation block in entity.json. This page covers the settings you can adjust.
Portway builds on OpenAPI 3.2, which gives the reference room to describe things earlier versions of the format could not:
- QUERY endpoints appear as native
queryoperations MERGEendpoints appear underadditionalOperationsrather than borrowing another verb's name- Namespaces become the tag structure
Deprecatedendpoints are shown as such- File uploads describe their multipart encoding
- Every error points at one shared schema, reused as a single media type
- Each response carries both its status phrase and an explanation of what it means
All of it follows from your endpoint definitions, so there is usually nothing extra to configure.
Global OpenAPI configuration
Configure the title, contact details, and Scalar UI behaviour in appsettings.json:
{
"OpenApi": {
"Enabled": true,
"Title": "Portway: API Gateway",
"Version": "v1",
"Description": "This is Portway. A lightweight API gateway that connects your platforms to your data sources and services, with a simple and fast setup.",
"Contact": {
"Name": "Your Name",
"Email": "support@yourcompany.com"
},
"Footer": {
"Text": "Powered by Scalar",
"Target": "_blank",
"Url": "#"
},
"SecurityDefinition": {
"Name": "Bearer",
"Description": "Bearer token issued by Portway. Send it as: Authorization: Bearer {token}",
"In": "Header",
"Type": "Http",
"Scheme": "Bearer"
},
"EnableFilter": false,
"EnableValidator": true,
"ScalarTheme": "default",
"ScalarShowSidebar": true,
"ScalarHideDownloadButton": true,
"ScalarHideModels": true
}
}Configuration properties
| Property | Type | Description |
|---|---|---|
Enabled |
boolean | Enable/disable API documentation generation |
Title |
string | Main title shown in documentation header |
Version |
string | API version displayed in documentation |
Description |
string | Main API description (supports markdown formatting) |
Contact.Name |
string | Contact person or team name |
Contact.Email |
string | Support email address |
Footer.Text |
string | Text displayed in the documentation footer |
Footer.Target |
string | Link target behavior (_blank for new tab, _self for same tab) |
Footer.Url |
string | URL for the footer link |
SecurityDefinition.Name |
string | Name of the security scheme (e.g., "Bearer") |
SecurityDefinition.Description |
string | Description of the authentication method |
SecurityDefinition.In |
string | Location of the API key (Header, Query, Cookie). Applies only when Type is ApiKey |
SecurityDefinition.Type |
string | Type of security scheme (ApiKey, Http, OAuth2, OpenIdConnect) |
SecurityDefinition.Scheme |
string | Authentication scheme (e.g., "Bearer", "Basic") |
ForceHttpsInProduction |
boolean | Force HTTPS URLs in production environments |
ScalarTheme |
string | Scalar UI color theme |
ScalarLayout |
string | Scalar UI layout style (modern, classic) |
ScalarShowSidebar |
boolean | Show/hide the navigation sidebar |
ScalarHideDownloadButton |
boolean | Hide the OpenAPI spec download button |
ScalarHideModels |
boolean | Hide the Models/Schemas section |
ScalarHideClientButton |
boolean | Hide the client generation button |
ScalarHideTestRequestButton |
boolean | Hide the test request button |
Documentation configuration
Each entity can include a Documentation section to customize its OpenAPI representation:
{
"DatabaseObjectName": "Products",
"AllowedColumns": ["ItemCode", "Description", "Price"],
"Documentation": {
"TagDescription": "**Product Catalog**\n\nAccess the product catalog with detailed item information.",
"MethodDescriptions": {
"GET": "Query product catalog with filtering and pagination",
"POST": "Add new products to the catalog",
"PUT": "Update existing product information",
"DELETE": "Remove products from catalog"
},
"Examples": {
"GET": {
"count": 1,
"value": [
{ "ItemCode": "ITEM-001", "Description": "Widget", "Price": 9.99 }
]
}
}
}
}Documentation properties
| Property | Type | Required | Description |
|---|---|---|---|
TagDescription |
string | Yes | Main description for the endpoint group |
MethodDescriptions |
object | No | Specific descriptions for each HTTP method |
MethodDocumentation |
object | No | Longer per-method descriptions (Markdown supported) |
Examples |
object | No | A response example per HTTP method, shown verbatim in the reference instead of generated sample data |
When you provide an example under Examples, Portway shows exactly that payload for the method's successful response. It is a friendly way to make sure the reference reflects the shape your integration really returns, rather than a generated approximation of it. Leaving it out is perfectly fine too, in which case Portway falls back to sample data as before. Every endpoint type accepts Examples, so this works equally well for SQL, Proxy, Composite, Static, Webhook, and Files.
Retiring an endpoint
Endpoints rarely disappear overnight. Usually you want to tell people an endpoint is on its way out well before you delete it, and sometimes you need to take one out of service for an afternoon. Portway gives you two separate flags for those two situations, and it can be helpful to think of them as a signal and a switch.
Deprecated is the signal. Adding it to any entity.json marks every operation that endpoint contributes as deprecated in the document, which Scalar then renders with a strikethrough:
{
"DatabaseObjectName": "LegacyOrders",
"AllowedMethods": ["GET"],
"Deprecated": true
}Your callers keep working exactly as before, because the flag only touches the documentation. That makes it a comfortable way to announce a planned retirement while you give integrations time to migrate. Every endpoint type understands it: SQL, Proxy, Composite, Static, Webhook, and Files.
Switching an endpoint off
Enabled is the switch. When you set it to false, the endpoint stops serving:
{
"DatabaseObjectName": "LegacyOrders",
"AllowedMethods": ["GET"],
"Enabled": false
}Calls then receive 503 Service Unavailable in the shared error envelope, together with a Retry-After header so clients and caches know to wait rather than retry immediately:
{
"success": false,
"error": "This endpoint is temporarily disabled for scheduled maintenance."
}You will notice the endpoint is still listed in the OpenAPI document, marked deprecated with a [Disabled] prefix on its summary. This is deliberate: if it vanished from the reference, a temporary outage would be indistinguishable from a deletion, and your callers would have no way to tell which one they were looking at. Disabled endpoints are left out of the MCP tool list as well, and the change is picked up on the next configuration reload, so a restart is not needed.
Enabled defaults to true, which means leaving it out keeps your existing configuration exactly as it is. If you would like to see the behaviour first-hand, the Static/Production/Machines sample ships switched off as a worked example.
File upload encoding
File endpoints describe their upload as multipart/form-data and document how the file part itself is encoded. The media types come from the endpoint's AllowedExtensions. A reports endpoint limited to .pdf, .xlsx, and .csv therefore advertises exactly those three types, rather than a generic binary blob.
Leaving AllowedExtensions out is fine too. The part then falls back to application/octet-stream, which is still perfectly valid and simply tells callers less about what you accept.
Error responses
One of the nicer things about generating the reference from your configuration is that errors only have to be described once. The document registers two component schemas: ErrorResponse, which is the familiar { success, error } envelope, and ValidationErrorResponse, which adds a per-field details array for 422 responses. Every documented 4xx and 5xx response then points at one of those two.
Which status codes turn up on a given operation still depends on what that endpoint type can actually return, so a Static endpoint and a Files upload will not show the same list. Only the shape is shared. If you would like to see the envelope itself, the reference index walks through it.
Namespaces and tags
Namespaces do double duty in the reference: each one becomes a tag, and your operations are grouped underneath the namespace they belong to. NamespaceDisplayName sets the label you see in the sidebar, and Documentation.TagDescription fills in the text below it.
OpenAPI 3.2 also allows one tag to be nested under another, and Portway emits that relationship whenever a namespace contains a /. Nest the directories to get it: an endpoint at endpoints/SQL/WMS/Inbound/StagingBins has the namespace WMS/Inbound and routes at /api/{env}/WMS/Inbound/StagingBins. The tag carries parent, kind: nav, and a summary holding just the leaf segment. Missing intermediate tags are created for you, so only the leaf needs a TagDescription.
Scalar does not read parent yet, so today it lists WMS and WMS/Inbound as sibling groups rather than nesting one inside the other. The document is correct either way, and the sidebar will nest once Scalar implements the field. Progress is tracked in scalar#6866.
Schema discovery
For SQL endpoints, Portway reads column metadata from the database at startup. It connects to the first allowed environment listed in the endpoint's AllowedEnvironments. Non-SQL endpoints are not queried.
WARNING
If you're using Windows Authentication (Trusted_Connection=True) in your Environments, the IIS Application Pool identity needs permissions on every environment database. With SQL Authentication, each environment uses its own credentials instead.
Tag descriptions
Formatting guidelines
Use bold titles and descriptive content:
"TagDescription": "**Service Management**\n\nComprehensive service request lifecycle management. Track customer issues, assign technicians, and monitor progress."Include context and purpose
Provide clear information about what the endpoint does:
"TagDescription": "**Financial Data**\n\nRetrieve outstanding debtor information and payment tracking. Access critical financial data for accounts receivable management and cash flow analysis."Method descriptions
Standard CRUD operations
Provide clear, action-oriented descriptions:
"MethodDescriptions": {
"GET": "Query and retrieve records with OData filtering support",
"POST": "Create new records with validation and business rules",
"PUT": "Update existing records with partial or complete data",
"DELETE": "Remove records with referential integrity checks"
}Specialized operations
For stored procedures or custom operations:
"MethodDescriptions": {
"GET": "Retrieve service requests with status and assignment filtering",
"POST": "Create new service requests with automatic assignment logic",
"PUT": "Update service request status, priority, and assignment"
}Composite endpoints
For complex operations:
"MethodDescriptions": {
"POST": "Create complete sales orders with header and multiple order lines in a coordinated transaction"
}Documentation structure
All entity types support the same OpenAPI documentation structure through the Documentation section:
{
// ... entity configuration ...
"Documentation": {
"TagDescription": "**Tag Name**\n\nDescription of what this endpoint group does.",
"MethodDescriptions": {
"GET": "Description for GET operations",
"POST": "Description for POST operations",
"PUT": "Description for PUT operations",
"DELETE": "Description for DELETE operations"
}
}
}Markdown support
Supported elements
OpenAPI descriptions support the Github-flavoured markdown. It also allows for limited HTML-support (<br>, <p>).
- Bold text with
**text** - Italic text with
*text* Code blockswith backticks- Line breaks with
\n - Links with
[text](url)
Admonitions
Use special formatting for callouts:
"TagDescription": "**Product Catalog**\n\nAccess the product catalog with basic item information.\n> [!tip]> This endpoint doesn't and will never include complex price information."See the Scalar markdown reference for supported alert types.
Hidden endpoint handling
Endpoints you mark Hidden are left out of the OpenAPI document while continuing to serve requests as normal. This is handy for internal endpoints you would rather not advertise:
{
"Hidden": true
// The Documentation section is ignored for hidden endpoints
}Environment-Specific documentation
Documentation is automatically filtered by environment. Only endpoints available in the current environment appear in the OpenAPI documentation:
{
"AllowedEnvironments": ["prod", "dev"]
// Only appears in documentation for prod and dev environments
}Troubleshooting
Documentation not appearing
- Verify JSON syntax in entity.json
- Check that
Documentationsection is properly formatted - Ensure endpoint is not marked as
Hidden: true - Confirm endpoint is allowed in current environment
Markdown not rendering
- Use
\nfor line breaks in JSON strings - Escape special characters properly
- Test markdown formatting in a separate viewer
- Check for unclosed formatting tags
Missing method descriptions
- Ensure method names match exactly (case-sensitive)
- Verify methods are listed in
AllowedMethodsorMethods - Check that methods are supported for the endpoint type
Portway