{
  "openapi": "3.1.0",
  "info": {
    "title": "traffic.cx API",
    "version": "1.0.0",
    "description": "Read stats, manage sites, and send events and payments with a team API key. Guides: https://traffic.cx/docs/api"
  },
  "servers": [{ "url": "https://api.traffic.cx" }],
  "security": [{ "apiKey": [] }],
  "tags": [
    { "name": "Stats", "description": "Needs stats:read. https://traffic.cx/docs/stats-api" },
    {
      "name": "Sites",
      "description": "Needs sites:write (reads also with stats:read). https://traffic.cx/docs/sites-api"
    },
    { "name": "Events", "description": "Needs events:write. https://traffic.cx/docs/events-api" },
    {
      "name": "Payments",
      "description": "Needs payments:write. https://traffic.cx/docs/payments-api"
    }
  ],
  "paths": {
    "/v1/query": {
      "post": {
        "tags": ["Stats"],
        "summary": "Run a query",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Query" } } }
        },
        "responses": {
          "200": {
            "description": "The rows",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/QueryResponse" } }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "402": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/query/batch": {
      "post": {
        "tags": ["Stats"],
        "summary": "Run up to 12 queries",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["queries"],
                "properties": {
                  "queries": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 12,
                    "items": { "$ref": "#/components/schemas/Query" }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One answer per query, in order; a failed query has error and status in its place",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "oneOf": [
                          { "$ref": "#/components/schemas/QueryResponse" },
                          {
                            "type": "object",
                            "properties": {
                              "error": { "type": "string" },
                              "status": { "type": "integer" },
                              "path": { "type": "string" }
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/sites": {
      "get": {
        "tags": ["Stats", "Sites"],
        "summary": "List the team's sites",
        "responses": {
          "200": {
            "description": "The sites",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sites": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": { "type": "string", "example": "tcx_8f3k2m9q" },
                          "domain": { "type": "string" },
                          "name": { "type": ["string", "null"] },
                          "timezone": { "type": "string" },
                          "currency": { "type": "string" },
                          "team": { "type": "string" },
                          "locked": { "type": "boolean" },
                          "first_day": { "type": ["string", "null"], "format": "date" },
                          "last_day": { "type": ["string", "null"], "format": "date" },
                          "revenue": { "type": "boolean" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Sites"],
        "summary": "Add a site",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["domain"],
                "properties": {
                  "domain": { "type": "string", "example": "shop.example.com" },
                  "name": { "type": "string" },
                  "timezone": { "type": "string", "default": "UTC" },
                  "currency": { "type": "string", "default": "USD" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "$ref": "#/components/responses/Site" },
          "400": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/sites/{site}": {
      "parameters": [{ "$ref": "#/components/parameters/site" }],
      "get": {
        "tags": ["Stats", "Sites"],
        "summary": "Read a site's settings",
        "responses": {
          "200": { "$ref": "#/components/responses/Site" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      },
      "patch": {
        "tags": ["Sites"],
        "summary": "Change a site's settings",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": { "type": "string" },
                  "domain": { "type": "string" },
                  "timezone": { "type": "string" },
                  "currency": { "type": "string" },
                  "identity_mode": { "type": "string", "enum": ["cookieless", "cookie"] },
                  "include_subdomains": { "type": "boolean" },
                  "allowed_hostnames": { "type": "array", "items": { "type": "string" } },
                  "allow_datacenter": { "type": "boolean" },
                  "revenue_lookback_days": { "type": "integer" },
                  "features": { "type": "object" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/Site" },
          "400": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      },
      "delete": {
        "tags": ["Sites"],
        "summary": "Remove a site",
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/sites/{site}/realtime": {
      "parameters": [{ "$ref": "#/components/parameters/site" }],
      "get": {
        "tags": ["Stats"],
        "summary": "Current visitors and the latest events",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 5 }
          }
        ],
        "responses": {
          "200": {
            "description": "Visitors in the last 5 minutes and recent events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "visitors": { "type": "integer" },
                    "recent": { "type": "array", "items": { "type": "object" } }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/sites/{site}/goals": {
      "parameters": [{ "$ref": "#/components/parameters/site" }],
      "get": {
        "tags": ["Stats", "Sites"],
        "summary": "List goals",
        "responses": {
          "200": {
            "description": "The goals",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "goals": { "type": "array", "items": { "$ref": "#/components/schemas/Goal" } }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Sites"],
        "summary": "Add a goal",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/GoalInput" } }
          }
        },
        "responses": {
          "201": {
            "description": "The goal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "goal": { "$ref": "#/components/schemas/Goal" } }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/sites/{site}/goals/{id}": {
      "parameters": [
        { "$ref": "#/components/parameters/site" },
        { "$ref": "#/components/parameters/id" }
      ],
      "patch": {
        "tags": ["Sites"],
        "summary": "Change a goal",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/GoalInput" } }
          }
        },
        "responses": {
          "200": {
            "description": "The goal",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "goal": { "$ref": "#/components/schemas/Goal" } }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/Error" }
        }
      },
      "delete": {
        "tags": ["Sites"],
        "summary": "Remove a goal",
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/sites/{site}/funnels": {
      "parameters": [{ "$ref": "#/components/parameters/site" }],
      "get": {
        "tags": ["Stats"],
        "summary": "List saved funnels",
        "responses": {
          "200": {
            "description": "The funnels",
            "content": { "application/json": { "schema": { "type": "object" } } }
          }
        }
      }
    },
    "/v1/sites/{site}/funnels/{id}/run": {
      "parameters": [
        { "$ref": "#/components/parameters/site" },
        { "$ref": "#/components/parameters/id" }
      ],
      "post": {
        "tags": ["Stats"],
        "summary": "Run a saved funnel",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "date_range": { "$ref": "#/components/schemas/DateRange" },
                  "filters": {
                    "type": "array",
                    "items": { "$ref": "#/components/schemas/Filter" }
                  },
                  "window": { "type": "string", "enum": ["session", "1d", "7d", "30d"] }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Each step's visitors",
            "content": { "application/json": { "schema": { "type": "object" } } }
          }
        }
      }
    },
    "/v1/sites/{site}/annotations": {
      "parameters": [{ "$ref": "#/components/parameters/site" }],
      "get": {
        "tags": ["Stats", "Sites"],
        "summary": "List chart notes",
        "parameters": [
          { "name": "from", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "to", "in": "query", "schema": { "type": "string", "format": "date-time" } }
        ],
        "responses": {
          "200": {
            "description": "The notes",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "annotations": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Annotation" }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Sites"],
        "summary": "Add a chart note",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/AnnotationInput" } }
          }
        },
        "responses": {
          "201": {
            "description": "The note",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "annotation": { "$ref": "#/components/schemas/Annotation" } }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/sites/{site}/annotations/{id}": {
      "parameters": [
        { "$ref": "#/components/parameters/site" },
        { "$ref": "#/components/parameters/id" }
      ],
      "patch": {
        "tags": ["Sites"],
        "summary": "Change a chart note",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": { "schema": { "$ref": "#/components/schemas/AnnotationInput" } }
          }
        },
        "responses": {
          "200": {
            "description": "The note",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "annotation": { "$ref": "#/components/schemas/Annotation" } }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/Error" }
        }
      },
      "delete": {
        "tags": ["Sites"],
        "summary": "Remove a chart note",
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/events": {
      "post": {
        "tags": ["Events"],
        "summary": "Send one event, or up to 25",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  { "$ref": "#/components/schemas/Event" },
                  {
                    "type": "object",
                    "required": ["events"],
                    "properties": {
                      "events": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 25,
                        "items": { "$ref": "#/components/schemas/Event" }
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "What became of each event",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    { "$ref": "#/components/schemas/EventResult" },
                    {
                      "type": "object",
                      "properties": {
                        "results": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/EventResult" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/payments": {
      "post": {
        "tags": ["Payments"],
        "summary": "Record or update a payment",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["site", "transaction_id", "amount", "currency"],
                "properties": {
                  "site": { "type": "string" },
                  "transaction_id": { "type": "string", "maxLength": 255 },
                  "amount": { "type": "number", "exclusiveMinimum": 0 },
                  "currency": { "type": "string", "example": "USD" },
                  "kind": {
                    "type": "string",
                    "enum": ["one_time", "subscription_start", "renewal", "refund"],
                    "default": "one_time"
                  },
                  "refund_of": { "type": "string" },
                  "timestamp": {
                    "oneOf": [{ "type": "string", "format": "date-time" }, { "type": "integer" }]
                  },
                  "pageview_id": { "type": "string" },
                  "tcx_vid": { "type": "string" },
                  "email": { "type": "string" },
                  "customer_id": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/PaymentResult" } }
            }
          },
          "201": {
            "description": "Recorded",
            "content": {
              "application/json": { "schema": { "$ref": "#/components/schemas/PaymentResult" } }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "403": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/payments/{transaction_id}": {
      "delete": {
        "tags": ["Payments"],
        "summary": "Remove a payment",
        "parameters": [
          {
            "name": "transaction_id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          { "name": "site", "in": "query", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "A team API key, tcx_sk_…, created in Team → API keys"
      }
    },
    "parameters": {
      "site": {
        "name": "site",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "example": "tcx_8f3k2m9q" }
      },
      "id": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      }
    },
    "responses": {
      "Error": {
        "description": "An error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["error"],
              "properties": {
                "error": { "type": "string" },
                "path": { "type": "string", "description": "The field at fault, e.g. metrics[0]" },
                "code": { "type": "string", "examples": ["plan_required", "rate_limited"] },
                "retry_after": { "type": "integer", "description": "Seconds to wait, on a 429" }
              }
            }
          }
        }
      },
      "Ok": {
        "description": "Done",
        "content": {
          "application/json": {
            "schema": { "type": "object", "properties": { "ok": { "const": true } } }
          }
        }
      },
      "Site": {
        "description": "The site",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "site": {
                  "type": "object",
                  "properties": {
                    "id": { "type": "string" },
                    "team": { "type": "string" },
                    "domain": { "type": "string" },
                    "name": { "type": ["string", "null"] },
                    "timezone": { "type": "string" },
                    "currency": { "type": "string" },
                    "identity_mode": { "type": "string" },
                    "include_subdomains": { "type": "boolean" },
                    "allowed_hostnames": { "type": "array", "items": { "type": "string" } },
                    "created_at": { "type": "string", "format": "date-time" }
                  }
                },
                "role": { "type": "string" }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "DateRange": {
        "oneOf": [
          {
            "type": "string",
            "enum": [
              "realtime",
              "today",
              "yesterday",
              "7d",
              "28d",
              "30d",
              "91d",
              "month",
              "last_month",
              "year",
              "12mo",
              "all"
            ]
          },
          {
            "type": "array",
            "items": { "type": "string", "format": "date" },
            "minItems": 2,
            "maxItems": 2
          }
        ]
      },
      "Filter": {
        "type": "array",
        "description": "[operator, dimension, values]",
        "prefixItems": [
          {
            "type": "string",
            "enum": ["is", "is_not", "contains", "contains_not", "matches", "matches_not"]
          },
          { "type": "string", "example": "visit:country" },
          { "type": "array", "items": { "type": "string" } }
        ],
        "minItems": 3,
        "maxItems": 3
      },
      "Query": {
        "type": "object",
        "required": ["site", "date_range", "metrics"],
        "properties": {
          "site": { "type": "string" },
          "date_range": { "$ref": "#/components/schemas/DateRange" },
          "metrics": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "visitors",
                "visits",
                "pageviews",
                "views_per_visit",
                "bounce_rate",
                "visit_duration",
                "events",
                "conversions",
                "conversion_rate",
                "time_on_page",
                "scroll_depth",
                "exit_rate",
                "percentage",
                "revenue",
                "refunds",
                "customers",
                "purchase_rate",
                "rpv",
                "aov",
                "ltv"
              ]
            }
          },
          "dimensions": {
            "type": "array",
            "maxItems": 3,
            "items": {
              "type": "string",
              "examples": ["visit:channel", "event:page", "event:props:plan", "time:day"]
            }
          },
          "filters": { "type": "array", "items": { "$ref": "#/components/schemas/Filter" } },
          "order_by": {
            "type": "array",
            "items": {
              "type": "array",
              "prefixItems": [{ "type": "string" }, { "type": "string", "enum": ["asc", "desc"] }]
            }
          },
          "include": {
            "type": "object",
            "properties": {
              "comparison": {
                "oneOf": [
                  { "type": "string", "enum": ["previous_period", "year_over_year"] },
                  { "type": "array", "items": { "type": "string", "format": "date" } },
                  { "type": "null" }
                ]
              },
              "match_day_of_week": { "type": "boolean" },
              "total_rows": { "type": "boolean" }
            }
          },
          "pagination": {
            "type": "object",
            "properties": {
              "limit": { "type": "integer", "maximum": 1000 },
              "offset": { "type": "integer" }
            }
          },
          "attribution": { "type": "string", "enum": ["last_non_direct", "first_touch"] }
        }
      },
      "QueryResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "dimensions": { "type": "array", "items": { "type": "string" } },
                "metrics": { "type": "array", "items": { "type": ["number", "null"] } },
                "comparison": {
                  "type": "object",
                  "properties": {
                    "dimensions": { "type": "array", "items": { "type": "string" } },
                    "metrics": { "type": "array", "items": { "type": ["number", "null"] } },
                    "change": { "type": "array", "items": { "type": ["number", "null"] } }
                  }
                }
              }
            }
          },
          "meta": {
            "type": "object",
            "properties": {
              "site": { "type": "string" },
              "timezone": { "type": "string" },
              "currency": { "type": "string" },
              "date_range": { "type": "object" },
              "comparison_range": { "type": "object" },
              "total_rows": { "type": "integer" },
              "sampled": { "const": false },
              "query_ms": { "type": "integer" },
              "cached": { "type": "boolean" }
            }
          }
        }
      },
      "GoalInput": {
        "type": "object",
        "properties": {
          "kind": { "type": "string", "enum": ["event", "pageview", "scroll"] },
          "match": { "type": "string", "examples": ["signup", "/pricing", "/blog/**"] },
          "scroll": { "type": "integer", "minimum": 1, "maximum": 100 },
          "name": { "type": "string" }
        }
      },
      "Goal": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "name": { "type": "string" },
          "kind": { "type": "string" },
          "match": { "type": "string" }
        }
      },
      "AnnotationInput": {
        "type": "object",
        "properties": {
          "at": { "type": "string", "format": "date-time" },
          "text": { "type": "string" }
        }
      },
      "Annotation": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "at": { "type": "string", "format": "date-time" },
          "text": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "created_by": { "type": ["string", "null"] }
        }
      },
      "Event": {
        "type": "object",
        "required": ["site", "url", "visitor_ip", "user_agent"],
        "properties": {
          "site": { "type": "string" },
          "name": { "type": "string", "default": "pageview", "maxLength": 120 },
          "url": { "type": "string", "format": "uri" },
          "referrer": { "type": "string" },
          "props": {
            "type": "object",
            "additionalProperties": { "type": ["string", "number", "boolean"] }
          },
          "pageview_id": { "type": "string", "pattern": "^[0-9a-f]{16}$" },
          "tcx_vid": { "type": "string", "pattern": "^[0-9a-f]{32}$" },
          "visitor_ip": { "type": "string" },
          "user_agent": { "type": "string", "maxLength": 512 }
        }
      },
      "EventResult": {
        "type": "object",
        "properties": {
          "result": {
            "type": "string",
            "examples": ["accepted", "dropped:bot_ua", "dropped:hostname"]
          },
          "pageview_id": {
            "type": "string",
            "description": "For pageviews: tie later events and payments to this visit"
          }
        }
      },
      "PaymentResult": {
        "type": "object",
        "properties": {
          "ok": { "const": true },
          "transaction_id": { "type": "string" },
          "attributed": { "type": "boolean" },
          "amount_reporting": { "type": "string" },
          "currency_reporting": { "type": "string" },
          "channel": { "type": "string" },
          "source": { "type": "string" },
          "duplicate_of": { "type": "string" }
        }
      }
    }
  }
}
