API Portal Design Skill
When to Use This Skill
Use this skill when:
- Api Portal Design tasks - Working on api documentation and developer portal design
- Planning or design - Need guidance on Api Portal Design approaches
- Best practices - Want to follow established patterns and standards
Overview
Design comprehensive API documentation and developer portals for exceptional developer experience.
MANDATORY: Documentation-First Approach
Before designing API portals:
- Invoke
docs-management skill for API documentation patterns
- Verify OpenAPI/AsyncAPI standards via MCP servers (context7)
- Base guidance on industry API documentation best practices
Developer Portal Architecture
Developer Portal Components:
┌─────────────────────────────────────────────────────────────────────────────┐
│ Developer Portal │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Getting │ │ API │ │ Code │ │ API │ │
│ │ Started │ │ Reference │ │ Examples │ │ Console │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ SDKs & │ │ Change │ │ Status │ │ Support │ │
│ │ Libraries │ │ Log │ │ Page │ │ Center │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Authentication & API Keys │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Portal Content Structure
Essential Sections
| Section |
Purpose |
Priority |
| Getting Started |
First-time user guide |
P0 |
| Authentication |
How to authenticate |
P0 |
| API Reference |
Complete endpoint docs |
P0 |
| Code Examples |
Copy-paste samples |
P0 |
| SDKs |
Client libraries |
P1 |
| Changelog |
Version history |
P1 |
| Rate Limits |
Usage constraints |
P1 |
| Errors |
Error handling guide |
P1 |
| Webhooks |
Event notifications |
P2 |
| Best Practices |
Usage recommendations |
P2 |
Getting Started Guide
# Getting Started
Get up and running with the [Product] API in 5 minutes.
## Prerequisites
- An account on [Product] ([Sign up free](link))
- An API key ([Get your key](link))
- Basic knowledge of REST APIs
## Quick Start
### 1. Get Your API Key
1. Log in to your [Product] dashboard
2. Navigate to **Settings → API Keys**
3. Click **Create New Key**
4. Copy your key (you won't see it again!)
### 2. Make Your First Request
```bash
curl -X GET "https://api.example.com/v1/users/me" \
-H "Authorization: Bearer YOUR_API_KEY"
Response:
{
"id": "usr_123abc",
"email": "developer@example.com",
"name": "Jane Developer",
"created_at": "2025-01-15T10:30:00Z"
}
3. Explore the API
Next Steps
Need Help?
Authentication Documentation
# Authentication
All API requests require authentication using Bearer tokens.
## API Keys
API keys are long-lived credentials for server-to-server communication.
### Creating API Keys
1. Go to **Dashboard → Settings → API Keys**
2. Click **Create New Key**
3. Give it a descriptive name
4. Select the appropriate permissions
5. Copy and securely store the key
### Using API Keys
Include your API key in the `Authorization` header:
```bash
curl -X GET "https://api.example.com/v1/resource" \
-H "Authorization: Bearer YOUR_API_KEY"
Key Security Best Practices
| Do |
Don't |
| Store keys in environment variables |
Commit keys to source control |
| Use separate keys per environment |
Share keys between services |
| Rotate keys regularly |
Use keys in client-side code |
| Set minimum required permissions |
Use admin keys for all operations |
OAuth 2.0
For user-facing applications, use OAuth 2.0 for secure delegated access.
Authorization Code Flow
┌──────────┐ ┌──────────┐
│ Client │ │ Auth │
│ App │ │ Server │
└────┬─────┘ └────┬─────┘
│ │
│ 1. Redirect to authorization endpoint │
│─────────────────────────────────────────►│
│ │
│ 2. User authenticates and consents │
│ │
│ 3. Redirect back with authorization code │
│◄─────────────────────────────────────────│
│ │
│ 4. Exchange code for tokens │
│─────────────────────────────────────────►│
│ │
│ 5. Return access_token and refresh_token │
│◄─────────────────────────────────────────│
│ │
OAuth Endpoints
| Endpoint |
URL |
| Authorization |
https://auth.example.com/oauth/authorize |
| Token |
https://auth.example.com/oauth/token |
| Revoke |
https://auth.example.com/oauth/revoke |
Scopes
| Scope |
Description |
read:users |
Read user information |
write:users |
Create and update users |
read:orders |
Read order data |
write:orders |
Create and modify orders |
Token Refresh
curl -X POST "https://auth.example.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=YOUR_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
OpenAPI Specification Template
openapi: 3.1.0
info:
title: Product API
version: 1.0.0
description: |
The Product API provides programmatic access to [Product] features.
## Authentication
All endpoints require authentication via Bearer token.
Get your API key from the [Dashboard](https://dashboard.example.com).
## Rate Limiting
- Standard: 100 requests/minute
- Premium: 1000 requests/minute
See [Rate Limits](/docs/rate-limits) for details.
contact:
name: API Support
email: api-support@example.com
url: https://example.com/support
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: https://api.example.com/v1
description: Production
- url: https://api-staging.example.com/v1
description: Staging
security:
- bearerAuth: []
tags:
- name: Users
description: User management operations
- name: Orders
description: Order processing operations
paths:
/users:
get:
tags: [Users]
operationId: listUsers
summary: List all users
description: |
Returns a paginated list of users in your organization.
Results are sorted by creation date, newest first.
parameters:
- name: limit
in: query
description: Maximum number of results to return
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: cursor
in: query
description: Pagination cursor from previous response
schema:
type: string
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/UserList'
examples:
default:
summary: Example response
value:
data:
- id: "usr_123"
email: "jane@example.com"
name: "Jane Doe"
created_at: "2025-01-15T10:30:00Z"
has_more: true
next_cursor: "cur_abc123"
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/RateLimited'
post:
tags: [Users]
operationId: createUser
summary: Create a user
description: Creates a new user in your organization.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
examples:
basic:
summary: Basic user creation
value:
email: "jane@example.com"
name: "Jane Doe"
with_metadata:
summary: User with metadata
value:
email: "jane@example.com"
name: "Jane Doe"
metadata:
department: "Engineering"
role: "Developer"
responses:
'201':
description: User created
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'409':
description: User already exists
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/users/{userId}:
get:
tags: [Users]
operationId: getUser
summary: Get a user
description: Retrieves a user by their ID.
parameters:
- name: userId
in: path
required: true
description: The user's unique identifier
schema:
type: string
pattern: '^usr_[a-zA-Z0-9]+$'
example: usr_123abc
responses:
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFound'
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
Use your API key as the Bearer token.
Example: `Authorization: Bearer sk_live_abc123`
schemas:
User:
type: object
required: [id, email, created_at]
properties:
id:
type: string
description: Unique identifier for the user
example: usr_123abc
email:
type: string
format: email
description: User's email address
example: jane@example.com
name:
type: string
description: User's display name
example: Jane Doe
created_at:
type: string
format: date-time
description: When the user was created
example: "2025-01-15T10:30:00Z"
metadata:
type: object
additionalProperties: true
description: Custom key-value pairs
UserList:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
has_more:
type: boolean
description: Whether more results are available
next_cursor:
type: string
description: Cursor for fetching next page
CreateUserRequest:
type: object
required: [email]
properties:
email:
type: string
format: email
name:
type: string
metadata:
type: object
additionalProperties: true
Error:
type: object
required: [error]
properties:
error:
type: object
required: [code, message]
properties:
code:
type: string
description: Error code
example: invalid_request
message:
type: string
description: Human-readable error message
example: The email field is required
details:
type: array
items:
type: object
properties:
field:
type: string
message:
type: string
responses:
BadRequest:
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: invalid_request
message: Validation failed
details:
- field: email
message: Invalid email format
Unauthorized:
description: Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: unauthorized
message: Invalid or missing API key
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: not_found
message: User not found
RateLimited:
description: Rate limit exceeded
headers:
X-RateLimit-Limit:
schema:
type: integer
description: Request limit per minute
X-RateLimit-Remaining:
schema:
type: integer
description: Remaining requests in current window
X-RateLimit-Reset:
schema:
type: integer
description: Unix timestamp when limit resets
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: rate_limited
message: Too many requests. Please retry after 60 seconds.
Error Documentation
# Error Handling
The API uses conventional HTTP response codes and returns detailed error information.
## HTTP Status Codes
| Code | Meaning |
|------|---------|
| 200 | Success |
| 201 | Created |
| 204 | No Content |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Invalid or missing credentials |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 409 | Conflict - Resource already exists |
| 422 | Unprocessable - Validation failed |
| 429 | Too Many Requests - Rate limited |
| 500 | Internal Error - Server-side issue |
## Error Response Format
```json
{
"error": {
"code": "invalid_request",
"message": "The email field is required",
"request_id": "req_abc123",
"details": [
{
"field": "email",
"message": "This field is required"
}
]
}
}
Error Codes Reference
Authentication Errors
| Code |
Description |
Resolution |
unauthorized |
Missing or invalid API key |
Check your API key is correct |
token_expired |
Access token has expired |
Refresh your token |
insufficient_scope |
Token lacks required scope |
Request additional scopes |
Validation Errors
| Code |
Description |
Resolution |
invalid_request |
Request body is malformed |
Check JSON syntax |
validation_failed |
One or more fields invalid |
See details array |
missing_required_field |
Required field not provided |
Include all required fields |
Resource Errors
| Code |
Description |
Resolution |
not_found |
Resource doesn't exist |
Verify the ID is correct |
already_exists |
Resource already exists |
Use existing resource or change identifier |
resource_locked |
Resource is being modified |
Retry after a short delay |
Handling Errors in Code
csharp
try
{
var user = await client.Users.GetAsync(userId, ct);
}
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
_logger.LogWarning("User {UserId} not found", userId);
return NotFound();
}
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests)
{
var retryAfter = ex.Headers.RetryAfter?.Delta ?? TimeSpan.FromSeconds(60);
await Task.Delay(retryAfter, ct);
// Retry request
}
catch (ApiException ex)
{
_logger.LogError(ex, "API error: {Code} - {Message}", ex.Error.Code, ex.Error.Message);
throw;
}
TypeScript
try {
const user = await client.users.get(userId);
} catch (error) {
if (error instanceof ApiError) {
switch (error.code) {
case 'not_found':
console.warn(`User ${userId} not found`);
return null;
case 'rate_limited':
await sleep(error.retryAfter ?? 60000);
return client.users.get(userId); // Retry
default:
console.error(`API error: ${error.code} - ${error.message}`);
throw error;
}
}
throw error;
}
Code Examples Section
# Code Examples
Ready-to-use examples in popular languages.
## Create a User
### cURL
```bash
curl -X POST "https://api.example.com/v1/users" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"name": "Jane Doe"
}'
C# (.NET)
using var client = new ProductApiClient(apiKey);
var user = await client.Users.CreateAsync(new CreateUserRequest
{
Email = "jane@example.com",
Name = "Jane Doe"
});
Console.WriteLine($"Created user: {user.Id}");
TypeScript
import { ProductApi } from '@example/sdk';
const client = new ProductApi({ apiKey: process.env.API_KEY });
const user = await client.users.create({
email: 'jane@example.com',
name: 'Jane Doe',
});
console.log(`Created user: ${user.id}`);
Python
from example_sdk import ProductApi
client = ProductApi(api_key=os.environ["API_KEY"])
user = client.users.create(
email="jane@example.com",
name="Jane Doe"
)
print(f"Created user: {user.id}")
Portal Tooling Options
| Tool |
Type |
Best For |
| Stoplight |
Hosted |
Design-first, collaboration |
| Redocly |
Hosted/Self |
OpenAPI rendering |
| ReadMe |
Hosted |
Full portal, interactive |
| SwaggerHub |
Hosted |
Swagger ecosystem |
| Scalar |
Open Source |
Modern, customizable |
| Docusaurus + Plugin |
Open Source |
Full control |
Best Practices
Developer Experience Principles
| Principle |
Implementation |
| Time to First Call |
Minimize steps to make first API call |
| Copy-Paste Ready |
All examples should work immediately |
| Error Messages |
Clear, actionable error responses |
| Consistency |
Same patterns across all endpoints |
| Discoverability |
Easy to find and navigate |
Documentation Quality Checklist
Workflow
When designing API portals:
- Define Audience: Who will use the API?
- Structure Content: Organize by user journey
- Write OpenAPI Spec: Complete specification
- Add Examples: Working code in target languages
- Build Portal: Choose tooling, implement
- Test with Users: Validate time-to-first-call
- Iterate: Improve based on feedback
References
For detailed guidance:
Last Updated: 2025-12-26