Skip to main content

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:

ObjectRequired Fields
BidRequestid, imp
Impid, native
Nativerequest

View complete required fields →

Data Type Validation

String Fields

Must be valid strings with appropriate length:

FieldMax LengthPattern
id64 charsAlphanumeric + -_
imp[].id64 charsAlphanumeric + -_
site.domain253 charsValid domain
app.bundle253 charsValid 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:

FieldMinMaxDescription
native.request110000Width in pixels
native.request110000Height in pixels
tmax1005000Timeout 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:

FieldMinMaxPrecision
imp.bidfloor0.001000.002 decimals
device.geo.lat-90.090.06 decimals
device.geo.lon-180.0180.06 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:

FieldMin ItemsMax ItemsItem Type
imp110object
site.cat050string
// ✅ 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:

FieldAllowed ValuesDescription
at1, 2Auction type
device.devicetype1-7Device 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_url must be valid HTTPS URL
  • id must 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.confidence must be between 0.0 and 1.0
  • sentiment.score must be between -1.0 and 1.0
  • sentiment.value must 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

CodeHTTP StatusDescription
INVALID_REQUEST400Malformed request structure
MISSING_REQUIRED_FIELD400Required field missing
INVALID_FIELD_VALUE400Field value invalid
INVALID_FIELD_TYPE400Field has wrong data type
UNSUPPORTED_FORMAT400Format not supported

View complete error codes →

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

Support

For validation issues:

  • Check the error.details.field to identify the problem field
  • Review validation rules in this guide
  • Use test mode to validate without affecting production
  • Contact support with request_id if issues persist