Validation Rules
Complete specification of validation rules applied to OpenRTB 2.6 bid requests by the Affinity AI Bid API.
Overview
The Bid API performs comprehensive validation on all incoming requests to ensure
data quality and prevent errors. Requests that fail validation are rejected with
a 400 Bad Request error containing details about the validation failure.
Validation Process
JSON Structure Validation
Valid JSON Required
Request body must be valid JSON:
// ✅ Valid
{
"id": "req-001",
"imp": [...]
}
// ❌ Invalid - Trailing comma
{
"id": "req-001",
"imp": [...],
}
// ❌ Invalid - Single quotes
{
'id': 'req-001',
'imp': [...]
}
Error Response:
{
"error": {
"code": "INVALID_REQUEST",
"message": "Invalid JSON format",
"details": {
"reason": "Unexpected token at position 45"
}
}
}
Content-Type Header
Request must include proper Content-Type header:
Content-Type: application/json
Field Presence Validation
Required Fields
All required fields must be present:
| Object | Required Fields |
|---|---|
| BidRequest | id, imp |
| Imp | id, native |
| Native | request |
View complete required fields →
Data Type Validation
String Fields
Must be valid strings with appropriate length:
| Field | Max Length | Pattern |
|---|---|---|
id | 64 chars | Alphanumeric + -_ |
imp[].id | 64 chars | Alphanumeric + -_ |
site.domain | 253 chars | Valid domain |
app.bundle | 253 chars | Valid bundle ID |
// ✅ Valid
{
"id": "req-2024-01-15-abc123"
}
// ❌ Invalid - Too long
{
"id": "req-very-long-id-that-exceeds-the-maximum-allowed-length-of-64-characters-and-will-be-rejected"
}
// ❌ Invalid - Invalid characters
{
"id": "req-001@#$%"
}
Integer Fields
Must be valid integers within allowed ranges:
| Field | Min | Max | Description |
|---|---|---|---|
native.request | 1 | 10000 | Width in pixels |
native.request | 1 | 10000 | Height in pixels |
tmax | 100 | 5000 | Timeout in milliseconds |
// ✅ Valid
{
"native": { "request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}" }
}
// ❌ Invalid - Exceeds maximum
{
"native": { "request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}" }
}
Float Fields
Must be valid floating-point numbers:
| Field | Min | Max | Precision |
|---|---|---|---|
imp.bidfloor | 0.00 | 1000.00 | 2 decimals |
device.geo.lat | -90.0 | 90.0 | 6 decimals |
device.geo.lon | -180.0 | 180.0 | 6 decimals |
// ✅ Valid
{
"bidfloor": 0.50
}
// ❌ Invalid - Negative bid floor
{
"bidfloor": -0.50
}
// ❌ Invalid - String instead of float
{
"bidfloor": "0.50"
}
Array Fields
Must be valid arrays with appropriate content:
| Field | Min Items | Max Items | Item Type |
|---|---|---|---|
imp | 1 | 10 | object |
site.cat | 0 | 50 | string |
// ✅ Valid
{
"imp": [
{"id": "imp-1", "native": {"request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}"}},
{"id": "imp-2", "native": {"request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}"}}
]
}
// ❌ Invalid - Empty array
{
"imp": []
}
// ❌ Invalid - Too many items
{
"imp": [
{"id": "imp-1", ...},
{"id": "imp-2", ...},
// ... 9 more items
{"id": "imp-12", ...}
]
}
Enum Fields
Must be one of the allowed values:
| Field | Allowed Values | Description |
|---|---|---|
at | 1, 2 | Auction type |
device.devicetype | 1-7 | Device type |
user.gender | "M", "F", "O" | Gender |
// ✅ Valid
{
"at": 2
}
// ❌ Invalid - Not in enum
{
"at": 3
}
Business Logic Validation
Mutually Exclusive Fields
Some fields cannot be used together:
Site vs App
Only one of site or app can be present:
// ✅ Valid - Site only
{
"id": "req-001",
"imp": [...],
"site": {
"domain": "publisher.com"
}
}
// ✅ Valid - App only
{
"id": "req-002",
"imp": [...],
"app": {
"bundle": "com.example.app"
}
}
// ❌ Invalid - Both present
{
"id": "req-003",
"imp": [...],
"site": {
"domain": "publisher.com"
},
"app": {
"bundle": "com.example.app"
}
}
Error Response:
{
"error": {
"code": "INVALID_REQUEST",
"message": "Mutually exclusive fields",
"details": {
"reason": "Cannot specify both 'site' and 'app'"
}
}
}
Field Dependencies
Some fields require other fields to be present:
Range Validation
Some fields must satisfy range constraints:
Duration Ranges
minduration must be less than or equal to maxduration:
// ✅ Valid
{
"native": {
"request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}"
}
}
Bid Floor vs Price
// Request
{
"imp": [{
"id": "imp-1",
"native": {
"request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}"
},
"bidfloor": 1.00
}]
}
// ✅ Valid response
{
"seatbid": [{
"bid": [{
"impid": "imp-1",
"price": 1.50 // >= 1.00
}]
}]
}
// ❌ Invalid response
{
"seatbid": [{
"bid": [{
"impid": "imp-1",
"price": 0.50 // < 1.00
}]
}]
}
Uniqueness Validation
Request ID Uniqueness
Each id should be unique across requests (not enforced but recommended):
// ✅ Good practice
const requestId = `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
Impression ID Uniqueness
Each imp[].id must be unique within the same request:
// ✅ Valid
{
"id": "req-001",
"imp": [
{"id": "imp-1", ...},
{"id": "imp-2", ...}
]
}
// ❌ Invalid - Duplicate IDs
{
"id": "req-001",
"imp": [
{"id": "imp-1", ...},
{"id": "imp-1", ...}
]
}
Error Response:
{
"error": {
"code": "INVALID_REQUEST",
"message": "Duplicate impression ID",
"details": {
"field": "imp[1].id",
"reason": "Impression ID 'imp-1' already used in imp[0]"
}
}
}
Format-Specific Validation
Native Validation
{
"native": {
// Required
"request": "{...}", // Valid JSON string
// Optional
"ver": "1.2", // Valid version
"api": [3, 5] // Valid API framework IDs
}
}
The request field must contain valid Native Ads 1.2 JSON:
{
"native": {
"request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[...]}}"
}
}
Privacy Validation
GDPR Validation
When regs.ext.gdpr is 1, user.ext.consent should be provided:
// ✅ Valid
{
"regs": {
"ext": {
"gdpr": 1
}
},
"user": {
"ext": {
"consent": "CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA"
}
}
}
// ⚠️ Warning - GDPR without consent
{
"regs": {
"ext": {
"gdpr": 1
}
}
}
US Privacy Validation
regs.ext.us_privacy must be valid US Privacy string:
// ✅ Valid
{
"regs": {
"ext": {
"us_privacy": "1YNN"
}
}
}
// ❌ Invalid - Wrong format
{
"regs": {
"ext": {
"us_privacy": "invalid"
}
}
}
Affinity AI Extensions Validation
AdCP Format Validation
When declaring AdCP format support:
{
"imp": [
{
"ext": {
"aura": {
"adcpFormats": [
{
"agent_url": "https://creative.adcontextprotocol.org", // Valid HTTPS URL
"id": "display_300x250" // Non-empty string
}
]
}
}
}
]
}
Validation Rules:
agent_urlmust be valid HTTPS URLidmust be non-empty string- Array must contain at least one format
Context Enhancement Validation
When providing context signals:
{
"ext": {
"aura": {
"intent": {
"value": "information_seeking", // Non-empty string
"confidence": 0.92, // 0.0 - 1.0
"topics": ["travel", "golf"] // Array of strings
},
"sentiment": {
"value": "positive", // "positive", "negative", "neutral"
"score": 0.75 // -1.0 to 1.0
}
}
}
}
Validation Rules:
intent.confidencemust be between 0.0 and 1.0sentiment.scoremust be between -1.0 and 1.0sentiment.valuemust be one of: "positive", "negative", "neutral"
Error Response Format
All validation errors follow this format:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"details": {
"field": "path.to.field",
"reason": "Detailed explanation"
},
"request_id": "req-abc123"
}
}
Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
INVALID_REQUEST | 400 | Malformed request structure |
MISSING_REQUIRED_FIELD | 400 | Required field missing |
INVALID_FIELD_VALUE | 400 | Field value invalid |
INVALID_FIELD_TYPE | 400 | Field has wrong data type |
UNSUPPORTED_FORMAT | 400 | Format not supported |
Validation Best Practices
1. Validate Client-Side
Validate requests before sending to reduce errors:
function validateBidRequest(request) {
// Check required fields
if (!request.id) {
throw new Error('Missing required field: id')
}
if (!request.imp || request.imp.length === 0) {
throw new Error('Missing required field: imp')
}
// Check impression IDs are unique
const impIds = new Set()
for (const imp of request.imp) {
if (!imp.id) {
throw new Error('Missing required field: imp.id')
}
if (impIds.has(imp.id)) {
throw new Error(`Duplicate impression ID: ${imp.id}`)
}
impIds.add(imp.id)
}
// Check native format per impression
for (const imp of request.imp) {
if (!imp.native) {
throw new Error(`Impression ${imp.id} missing native object`)
}
}
return true
}
2. Use Test Mode
Test requests with test: 1 to validate without affecting production:
{
"id": "test-req-001",
"test": 1,
"imp": [...]
}
3. Log Validation Errors
Always log validation errors with request ID for debugging:
try {
const response = await sendBidRequest(request)
} catch (error) {
if (error.response?.status === 400) {
console.error('Validation error:', {
requestId: error.response.data.request_id,
code: error.response.data.error.code,
field: error.response.data.error.details?.field,
reason: error.response.data.error.details?.reason,
})
}
}
4. Handle Validation Errors Gracefully
Don't retry validation errors (4xx) - fix the request instead:
async function sendBidRequest(request) {
try {
return await axios.post(url, request)
} catch (error) {
if (error.response?.status >= 400 && error.response?.status < 500) {
// Client error - don't retry, log and fix
logValidationError(error)
throw error
} else if (error.response?.status >= 500) {
// Server error - retry with backoff
return retryWithBackoff(request)
}
}
}
Testing Validation
Valid Request Test
curl -X POST https://bid-aura.affinity.net/openrtb2 \
-H "Content-Type: application/json" \
-d '{
"id": "test-valid-001",
"test": 1,
"imp": [{
"id": "imp-1",
"native": { "request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}" }
}]
}'
Invalid Request Test
curl -X POST https://bid-aura.affinity.net/openrtb2 \
-H "Content-Type: application/json" \
-d '{
"imp": [{
"id": "imp-1",
"native": { "request": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":80}}]}}" }
}]
}'
Expected error:
{
"error": {
"code": "MISSING_REQUIRED_FIELD",
"message": "Required field missing",
"details": {
"field": "id",
"reason": "BidRequest must include 'id' field"
}
}
}
Next Steps
- Required Fields - Minimum field requirements
- Recommended Fields - Optional fields for better performance
- Error Handling - Error codes and retry logic
Support
For validation issues:
- Check the
error.details.fieldto identify the problem field - Review validation rules in this guide
- Use test mode to validate without affecting production
- Contact support with
request_idif issues persist