{
  "openapi": "3.1.0",
  "info": {
    "title": "NotPixel Monetization & Events HTTP API",
    "version": "3.1.0",
    "description": "Contextual ad selection, cache validation, and event tracking HTTP API for LLM applications, backends (Node.js, Python, Go, Java, PHP), and mobile apps."
  },
  "servers": [
    {
      "url": "https://api.notpixel.ai",
      "description": "Production API"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Liveness check",
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "timestamp"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pick": {
      "post": {
        "operationId": "pickAd",
        "summary": "Select a contextual ad via semantic auction",
        "description": "Selects the highest-ranking approved contextual ad using a locally generated 384-dimensional BGE vector or context processed remotely through NotPixel and settles the impression atomically. Pass a stable `requestId` per opportunity to make retries idempotent.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PickRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Auction decision (either a matched ad or ad: null on no-fill)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PickResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Publisher not found, inactive, or below minimum trust score",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "413": {
            "description": "Request body exceeds 16 KiB"
          },
          "429": {
            "description": "Request rate or remote context processing quota exceeded",
            "headers": {
              "Retry-After": {
                "description": "Seconds until retry is allowed",
                "schema": {
                  "type": "integer"
                }
              }
            }
          },
          "500": {
            "description": "Internal or billing error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "503": {
            "description": "Remote context processing unavailable or inference deadline exceeded; no auction is started"
          }
        }
      }
    },
    "/api/v1/ads/validate": {
      "post": {
        "operationId": "validateAd",
        "summary": "Validate a cached ad and revision before reuse",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValidateAdRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Validation result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "valid"
                  ],
                  "properties": {
                    "valid": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/events": {
      "post": {
        "operationId": "acknowledgeImpression",
        "summary": "Acknowledge an existing auctioned impression",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ImpressionEventRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Impression acknowledged",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Impression not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/click": {
      "post": {
        "operationId": "trackClick",
        "summary": "Track a click on an auctioned impression (idempotent per impressionId)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClickRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Click recorded or deduplicated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClickResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Impression not found or publisher inactive",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/convert": {
      "post": {
        "operationId": "trackConversion",
        "summary": "Track a post-click/impression conversion (idempotent when externalId is provided)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ConversionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Conversion recorded or deduplicated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversionResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Impression not found or publisher inactive",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/feedback": {
      "post": {
        "operationId": "submitFeedback",
        "summary": "Record user ad hide or policy violation report (idempotent per eventId)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Identical feedback event already recorded (replay)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedbackResponse"
                }
              }
            }
          },
          "201": {
            "description": "Feedback event recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedbackResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Impression not found or older than 30 days",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "eventId reused with different payload",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PickRequest": {
        "type": "object",
        "required": [
          "publisherId"
        ],
        "oneOf": [
          {
            "required": [
              "embedding"
            ]
          },
          {
            "required": [
              "input"
            ]
          }
        ],
        "additionalProperties": false,
        "properties": {
          "publisherId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Publisher public ID (e.g., pub_10565) or UUID"
          },
          "input": {
            "type": "string",
            "minLength": 1,
            "description": "Remote processing: context sent to NotPixel and its Cloudflare inference infrastructure. Sanitized and limited to 256 characters before inference. Supply input OR embedding, never both. Remote processing is subject to eligibility and usage limits."
          },
          "embedding": {
            "type": "array",
            "items": {
              "type": "number"
            },
            "minItems": 384,
            "maxItems": 384,
            "description": "Generated by the integrator in its own environment using Xenova/bge-small-en-v1.5 (q8), attention-mask mean pooling and L2 normalization. Exactly 384 finite values with at least one non-zero value. Do not send prompt text or substitute another 384-dimensional model."
          },
          "requestId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Optional client idempotency key per ad opportunity to prevent double-billing on retry"
          },
          "context": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional context metadata (lang, region, stack)"
          }
        }
      },
      "Ad": {
        "type": "object",
        "required": [
          "id",
          "headline",
          "disclosure",
          "cta"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "contentRevision": {
            "type": "integer",
            "minimum": 1
          },
          "category": {
            "type": "string",
            "enum": [
              "general",
              "health",
              "finance",
              "unsupported"
            ]
          },
          "disclaimer": {
            "type": "string"
          },
          "headline": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "disclosure": {
            "type": "string",
            "example": "Patrocinado"
          },
          "cta": {
            "type": "object",
            "required": [
              "label",
              "url"
            ],
            "properties": {
              "label": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "PickResponse": {
        "type": "object",
        "required": [
          "requestId",
          "ad"
        ],
        "properties": {
          "requestId": {
            "type": "string"
          },
          "impressionId": {
            "type": "string",
            "format": "uuid"
          },
          "ad": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Ad"
              },
              {
                "type": "null"
              }
            ]
          },
          "_meta": {
            "type": "object",
            "properties": {
              "noFillReason": {
                "type": "string",
                "enum": [
                  "no_candidates",
                  "budget_exhausted",
                  "below_min_relevance",
                  "below_floor_price",
                  "targeting_mismatch",
                  "creative_not_approved"
                ]
              },
              "priceCents": {
                "type": "integer"
              },
              "priceMilliCents": {
                "type": "integer"
              },
              "deduplicated": {
                "type": "boolean"
              },
              "auctionMode": {
                "type": "string",
                "enum": [
                  "live",
                  "shadow",
                  "legacy"
                ]
              }
            }
          }
        }
      },
      "ValidateAdRequest": {
        "type": "object",
        "required": [
          "adId",
          "contentRevision",
          "impressionId",
          "publisherId"
        ],
        "properties": {
          "adId": {
            "type": "string",
            "format": "uuid"
          },
          "contentRevision": {
            "type": "integer",
            "minimum": 1
          },
          "impressionId": {
            "type": "string",
            "format": "uuid"
          },
          "publisherId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        }
      },
      "ImpressionEventRequest": {
        "type": "object",
        "properties": {
          "impressionId": {
            "type": "string",
            "format": "uuid",
            "description": "Preferred: UUID returned by /api/v1/pick"
          },
          "event": {
            "type": "string",
            "enum": [
              "impression"
            ]
          },
          "adId": {
            "type": "string",
            "format": "uuid"
          },
          "contentRevision": {
            "type": "integer",
            "minimum": 1
          },
          "requestId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "publisherId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          }
        }
      },
      "ClickRequest": {
        "type": "object",
        "required": [
          "impressionId"
        ],
        "properties": {
          "impressionId": {
            "type": "string",
            "format": "uuid"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ClickResponse": {
        "type": "object",
        "required": [
          "ok",
          "clickId"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "clickId": {
            "type": "string",
            "format": "uuid"
          },
          "duplicate": {
            "type": "boolean"
          }
        }
      },
      "ConversionRequest": {
        "type": "object",
        "required": [
          "impressionId",
          "type"
        ],
        "properties": {
          "impressionId": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50,
            "example": "signup"
          },
          "valueCents": {
            "type": "integer",
            "minimum": 0
          },
          "externalId": {
            "type": "string",
            "maxLength": 255,
            "description": "Stable event ID from your system for idempotent deduplication"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ConversionResponse": {
        "type": "object",
        "required": [
          "ok",
          "duplicate"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "conversionId": {
            "type": "string",
            "format": "uuid"
          },
          "duplicate": {
            "type": "boolean"
          }
        }
      },
      "FeedbackRequest": {
        "type": "object",
        "required": [
          "eventId",
          "impressionId",
          "action"
        ],
        "properties": {
          "eventId": {
            "type": "string",
            "format": "uuid"
          },
          "impressionId": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "hide",
              "report"
            ]
          },
          "reason": {
            "type": "string",
            "enum": [
              "misleading",
              "unsafe",
              "inappropriate",
              "policy_violation"
            ],
            "description": "Required when action is \"report\""
          },
          "details": {
            "type": "string",
            "maxLength": 1000
          }
        }
      },
      "FeedbackResponse": {
        "type": "object",
        "required": [
          "ok",
          "eventId"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "eventId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  }
}
