19 KiB
OData Operation Template
How to Use This Template
Purpose: Document individual OData operations (CRUD, functions, actions) with complete request/response details.
When to Use:
- Creating detailed documentation for a specific OData operation
- Documenting CRUD operations with full request/response examples
- Documenting custom functions and actions
- Detailed operation documentation linked from Resource template
Instructions:
- Replace all [bracketed text] with your actual operation information
- Include complete, working HTTP request examples
- Show real response examples with actual data and status codes
- Document all possible status codes for this specific operation
- Include both success and error response examples
- Test the operation and verify all examples work
- Remove optional sections if not applicable
Cross-Reference: Use with OData Resource Template for resource context and OData Service Overview Template for service-level info.
Template Structure:
- Title & Introduction
- Operation Details (type, HTTP method, permission)
- Request (headers, parameters, body, examples)
- Response (headers, status codes, body, examples)
[Operation Name] ([HTTP Method])
[Provide comprehensive description of what this operation does. Include:
- What action is performed
- What is returned (if applicable)
- When to use this operation
- Important behaviors or side effects]
Example: "Creates a new employee record in the system with provided information. Automatically assigns unique employee ID and initializes default values (status: ACTIVE, creation timestamp). Triggers HR workflow notifications."
Usage
[Explain when and why to use this operation, including:
- Primary use cases and scenarios
- When to use alternative operations
- Important prerequisites or constraints
- Any special behaviors or workflows]
Use this operation to [describe scenario].
Key points:
- [Important characteristic or constraint]
- [Related operation if applicable]
- [Performance consideration if relevant]
- [Workflow impact or side effect if relevant]
Example: "Use this operation when adding a new employee to the system. Required fields include first name, last name, email, and department. Optional fields like hire date and salary can be provided.
Key points:
- Automatically generates unique EmployeeID
- Email must be unique across system
- Returns created employee in response body (if Prefer header included)
- Triggers HR workflow notifications to department manager
- Related operations: PATCH for updates, GET for retrieval"
Request
Operation Details
URI: [HTTP Method] [Path]
Example: POST /Employees, GET /Employees('E12345'), PATCH /Employees('E12345')
Operation Type: [CRUD/Function/Action]
| Aspect | Value |
|---|---|
| HTTP Method | [GET/POST/PUT/PATCH/DELETE] |
| Operation Type | [CRUD (standard)/Function (returns data)/Action (performs action)] |
| Resource | [Resource name] |
| Full URI Pattern | [Complete URI pattern with placeholders] |
Example:
| Aspect | Value |
|---|---|
| HTTP Method | POST |
| Operation Type | CRUD (Create) |
| Resource | Employees |
| Full URI Pattern | POST /Employees |
Required Permission
Permission: [Required role or permission level]
[Explanation of what this permission allows and why it's required]
Example: "Permission: ROLE_HR_MANAGER or ROLE_ADMIN
Only users with ROLE_HR_MANAGER or higher role can create employees. Lower roles like ROLE_HR_USER cannot call this operation."
Request Headers
| Header Name | Required | Possible Values | Description |
|---|---|---|---|
| [Header] | [Yes/No] | [Values] | [Description with format/examples] |
Example:
| Header Name | Required | Possible Values | Description |
|---|---|---|---|
| Authorization | Yes | Bearer {token} | OAuth2 authentication token. Format: Bearer {token}. Required for all operations. |
| Content-Type | Yes (POST/PUT/PATCH) | application/json | Media type of request body. Required for operations with request body. Value: application/json |
| Accept | No | application/json | Preferred response format. Default: application/json. Optional: specify other formats if supported. |
| Prefer | No | return=representation, return=minimal | OData preference. return=representation: include created/updated entity in response. return=minimal: response without body (faster). |
| If-Match | No | {ETag} | ETag for optimistic concurrency control. Format: quoted string (e.g., "abc123"). Required for safe concurrent updates on PUT/PATCH. |
| X-Request-ID | No | UUID | Optional request tracking ID. Format: any valid UUID. Example: 123e4567-e89b-12d3-a456-426614174000 |
Request Parameters
[Document all parameters passed to the operation, organized by location.]
Path Parameters
[If operation uses path parameters in URI]
| Parameter Name | Requirement | Data Type | Description | Location |
|---|---|---|---|---|
| [Name] | Required/Optional | [Type] | [Description with constraints, pattern, valid values] | Path |
Example:
| Parameter Name | Requirement | Data Type | Description | Location |
|---|---|---|---|---|
| EmployeeID | Required | String | Employee unique identifier. Pattern: E[0-9]{5}. Example: "E12345" | Path |
Query Parameters
[If operation uses query parameters]
| Parameter Name | Requirement | Data Type | Description | Location |
|---|---|---|---|---|
| $filter | Optional | String | OData filter expression for query. Example: FirstName eq 'John'. Used only in GET collection operations. | Query |
| $orderby | Optional | String | Sort order. Example: HireDate desc, LastName asc. Used in GET collection operations. | Query |
| $top | Optional | Integer | Maximum records to return (1-1000). Default: 50. Used for pagination. | Query |
| $skip | Optional | Integer | Records to skip for pagination. Default: 0. Used with $top. | Query |
| $select | Optional | String | Properties to include in response. Example: FirstName,LastName,Email. Reduces payload size. | Query |
| $expand | Optional | String | Navigate relationships and include related data. Example: Department,Manager. Limited to 3 levels deep. | Query |
Detailed Example for POST:
[No query parameters for POST operations - all data in request body]
Detailed Example for GET with Collection:
| Parameter Name | Requirement | Data Type | Description | Location |
|---|---|---|---|---|
| $filter | Optional | String | Filter expression. Example: $filter=Status eq 'ACTIVE' and Salary gt 100000 | Query |
| $orderby | Optional | String | Sort fields. Example: $orderby=HireDate desc,LastName asc | Query |
| $top | Optional | Integer | Max results (1-1000, default: 50). Example: $top=100 | Query |
| $skip | Optional | Integer | Records to skip for pagination. Example: $skip=200 | Query |
| $select | Optional | String | Properties to include. Example: $select=FirstName,LastName,Email | Query |
| $expand | Optional | String | Include related data. Example: $expand=Department,Manager | Query |
| $count | Optional | Boolean | Include total count. Returns entities with @odata.count. Value: true. Example: ?$count=true. Note: Path /Employees/$count returns only integer count. | Query |
Request Body
[For POST/PUT/PATCH operations with request body]
Format: [JSON structure with all properties]
Required Fields: [List of required fields]
Optional Fields: [List of optional fields]
| Property Name | Requirement | Data Type | Description | Constraints |
|---|---|---|---|---|
| [Property] | Required/Optional | [Type] | [Description] | [Min/max, format, valid values] |
Example:
Request body structure:
{
"FirstName": "string",
"LastName": "string",
"Email": "string",
"Department": "string",
"HireDate": "date",
"Salary": "decimal"
}
Required Fields: FirstName, LastName, Email, Department
Optional Fields: HireDate, Salary
| Property Name | Requirement | Data Type | Description | Constraints |
|---|---|---|---|---|
| FirstName | Required | String | Employee's first name | 1-50 characters, alphanumeric + spaces |
| LastName | Required | String | Employee's last name | 1-50 characters, alphanumeric + spaces |
| Email | Required | String | Corporate email address | Must be unique, valid email format (RFC 5322) |
| Department | Required | String | Department code | Valid: "SALES", "ENGINEERING", "FINANCE", "HR", "OPERATIONS" |
| HireDate | Optional | Date | Hire date in YYYY-MM-DD format | Cannot be future date, ISO 8601 format |
| Salary | Optional | Decimal | Annual salary in USD | Minimum: 20000, maximum: 10000000, 2 decimal places |
Request Example
[Provide complete, working HTTP request with all headers and body.]
Template:
[HTTP METHOD] [Path]?[Query Parameters] HTTP/1.1
Host: [Host]
Authorization: Bearer [token]
Content-Type: application/json
[Additional Headers]
[Request body if applicable]
Example - GET Collection with Filtering
GET /Employees?$filter=Status eq 'ACTIVE' and Department eq 'ENGINEERING'&$orderby=LastName asc&$top=50&$skip=0 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/json
Example - GET Single Resource
GET /Employees('E12345') HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Accept: application/json
Example - POST (Create)
POST /Employees HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Prefer: return=representation
{
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Department": "ENGINEERING",
"HireDate": "2024-01-15",
"Salary": 95000.00
}
Example - PATCH (Update)
PATCH /Employees('E12345') HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
If-Match: "abc123def456"
{
"Department": "SALES",
"Salary": 105000.00
}
Example - PUT (Replace)
PUT /Employees('E12345') HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Department": "SALES",
"HireDate": "2024-01-15",
"Salary": 105000.00
}
Example - DELETE
DELETE /Employees('E12345') HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Response
Response Headers
| Header Name | Description | Possible Values |
|---|---|---|
| [Header] | [What this header contains] | [Example values] |
Example:
| Header Name | Description | Possible Values |
|---|---|---|
| Content-Type | Response body media type | application/json |
| Location | URL of created/modified resource | [https://api.example.com/odata/v4/Employees('E12346](https://api.example.com/odata/v4/Employees('E12346)') |
| ETag | Entity tag for caching and concurrency control | "abc123def456" |
| OData-Version | OData protocol version used | 4.0 |
| Preference-Applied | Which Prefer preference was applied | return=representation, return=minimal |
Status Codes
| Status Code | Description | Conditions | Response Body |
|---|---|---|---|
| [Code] | [What status means] | [When this occurs] | [Type of response body] |
Example for GET (200 OK):
| Status Code | Description | Conditions | Response Body |
|---|---|---|---|
| 200 OK | Request successful | Entity/collection retrieved | Entity or collection data |
| 401 Unauthorized | Authentication required | Missing/invalid token | Error object |
| 403 Forbidden | Insufficient permissions | User lacks required role | Error object |
| 404 Not Found | Resource doesn't exist | Invalid ID/key | Error object |
| 500 Internal Server Error | Server error | Unhandled exception | Error object |
Example for POST (201 Created):
| Status Code | Description | Conditions | Response Body |
|---|---|---|---|
| 201 Created | Resource created successfully | Valid request, auto-generated ID | Created entity (if Prefer: return=representation) |
| 400 Bad Request | Validation error | Invalid data, missing required field | Error object with details |
| 401 Unauthorized | Authentication required | Missing/invalid token | Error object |
| 403 Forbidden | Insufficient permissions | User lacks ROLE_HR_MANAGER | Error object |
| 409 Conflict | Duplicate/constraint violation | Email already exists | Error object with details |
| 500 Internal Server Error | Server error | Database error | Error object |
Example for PATCH (204 No Content or 200 OK):
| Status Code | Description | Conditions | Response Body |
|---|---|---|---|
| 204 No Content | Update successful | Default response without Prefer header | Empty body |
| 200 OK | Update successful | Prefer: return=representation | Updated entity data |
| 400 Bad Request | Validation error | Invalid field values | Error object |
| 401 Unauthorized | Authentication required | Missing/invalid token | Error object |
| 403 Forbidden | Insufficient permissions | User lacks required role | Error object |
| 404 Not Found | Resource doesn't exist | Invalid ID | Error object |
| 412 Precondition Failed | Optimistic concurrency failure | If-Match ETag mismatch | Error object |
| 500 Internal Server Error | Server error | Database error | Error object |
Response Body (Successful)
[Document the successful response body structure, including all properties.]
{
"[property]": "[value or type]",
"[property]": "[value or type]"
}
Example for GET Single (200 OK):
{
"EmployeeID": "E12345",
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Department": "ENGINEERING",
"HireDate": "2020-06-01",
"Salary": 120000.00,
"Status": "ACTIVE",
"CreatedAt": "2020-06-01T09:00:00Z",
"LastModified": "2024-01-15T14:30:00Z"
}
Example for GET Collection (200 OK):
{
"value": [
{
"EmployeeID": "E12345",
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Status": "ACTIVE"
},
{
"EmployeeID": "E12346",
"FirstName": "Jane",
"LastName": "Smith",
"Email": "jane.smith@company.com",
"Status": "ACTIVE"
}
]
}
Example for POST (201 Created):
{
"EmployeeID": "E12346",
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Department": "ENGINEERING",
"HireDate": "2024-01-15",
"Salary": 95000.00,
"Status": "ACTIVE",
"CreatedAt": "2024-01-15T10:30:00Z",
"LastModified": "2024-01-15T10:30:00Z"
}
Complete Response Examples
Success Response (200 OK - GET)
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "abc123def456"
OData-Version: 4.0
{
"EmployeeID": "E12345",
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Department": "ENGINEERING",
"HireDate": "2020-06-01",
"Salary": 120000.00,
"Status": "ACTIVE",
"CreatedAt": "2020-06-01T09:00:00Z",
"LastModified": "2024-01-15T14:30:00Z"
}
Success Response (201 Created - POST)
HTTP/1.1 201 Created
Content-Type: application/json
Location: [https://api.example.com/odata/v4/Employees('E12346](https://api.example.com/odata/v4/Employees('E12346)')
ETag: "def789ghi123"
OData-Version: 4.0
{
"EmployeeID": "E12346",
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Department": "ENGINEERING",
"HireDate": "2024-01-15",
"Salary": 95000.00,
"Status": "ACTIVE",
"CreatedAt": "2024-01-15T10:30:00Z",
"LastModified": "2024-01-15T10:30:00Z"
}
Success Response (204 No Content - PATCH)
HTTP/1.1 204 No Content
Success Response (Collection - GET with $top/$skip)
HTTP/1.1 200 OK
Content-Type: application/json
OData-Version: 4.0
{
"value": [
{
"EmployeeID": "E12345",
"FirstName": "John",
"LastName": "Doe",
"Email": "john.doe@company.com",
"Department": "ENGINEERING",
"Status": "ACTIVE"
},
{
"EmployeeID": "E12346",
"FirstName": "Jane",
"LastName": "Smith",
"Email": "jane.smith@company.com",
"Department": "SALES",
"Status": "ACTIVE"
}
]
}
Error Response Examples
Error Response (400 Bad Request)
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed. See details for specific errors",
"details": [
{
"field": "Email",
"issue": "Email already exists in system",
"value": "john.doe@company.com",
"existingEmployeeId": "E10001"
},
{
"field": "Salary",
"issue": "Minimum salary must be at least 20000",
"value": "15000"
}
]
}
}
Error Response (401 Unauthorized)
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Authentication token missing, invalid, or expired",
"details": {
"reason": "Bearer token not provided in Authorization header"
}
}
}
Error Response (403 Forbidden)
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": {
"code": "INSUFFICIENT_PERMISSION",
"message": "Insufficient permissions for this operation",
"details": {
"requiredRole": "ROLE_HR_MANAGER",
"userRole": "ROLE_HR_USER",
"operation": "Create Employee"
}
}
}
Error Response (404 Not Found)
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error": {
"code": "NOT_FOUND",
"message": "Requested resource not found",
"details": {
"resourceType": "Employee",
"providedId": "E99999",
"suggestion": "Verify the employee ID exists and hasn't been deleted"
}
}
}
Error Response (409 Conflict - Duplicate)
HTTP/1.1 409 Conflict
Content-Type: application/json
{
"error": {
"code": "DUPLICATE_EMAIL",
"message": "Employee with provided email already exists",
"details": {
"email": "john.doe@company.com",
"existingEmployeeId": "E10001",
"existingEmployeeName": "John Doe"
}
}
}
Error Response (500 Internal Server Error)
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
"error": {
"code": "INTERNAL_SERVER_ERROR",
"message": "Server encountered an unexpected error",
"details": {
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-15T14:30:00Z",
"suggestion": "Contact support with provided trace ID"
}
}
}
Special Cases / Additional Notes
[Document any special behaviors, edge cases, or implementation notes]
Example:
- Soft delete: Employee records are marked with Status='TERMINATED', not physically deleted
- Automatic fields: EmployeeID, CreatedAt, and LastModified are auto-generated
- Optimistic concurrency: Use If-Match header with ETag to prevent lost updates
- Batch operations: This operation can be included in a $batch request
- Rate limiting: This operation counts as 1 request toward rate limit
Related Operations
Template Version: 1.0 Last Updated: 2025-11-21 Compliance: SAP API Style Guide Section 50