{
  "openapi": "3.1.0",
  "info": {
    "title": "GraphJSON API",
    "version": "2026-07-26",
    "summary": "Ingest JSON events and request GraphJSON visualization data or embed URLs.",
    "description": "Machine-readable contract for the documented public GraphJSON HTTP API. Authentication uses the api_key property in each JSON request body. See https://graphjson.com/docs/API/versioning-and-changelog for compatibility policy.",
    "license": {
      "name": "Proprietary",
      "url": "https://graphjson.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.graphjson.com",
      "description": "Production API"
    }
  ],
  "tags": [
    {
      "name": "Ingestion",
      "description": "Append timestamped JSON events to a collection."
    },
    {
      "name": "Visualization",
      "description": "Return visualization data or generated embed URLs."
    }
  ],
  "security": [],
  "paths": {
    "/api/log": {
      "post": {
        "operationId": "logEvent",
        "summary": "Log one event",
        "description": "Appends one serialized JSON object to a collection.",
        "tags": [
          "Ingestion"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LogEventRequest"
              },
              "examples": {
                "signup": {
                  "value": {
                    "api_key": "your_api_key",
                    "collection": "product_events",
                    "timestamp": 1785081600,
                    "json": "{\"event\":\"signup_completed\",\"event_id\":\"evt_01J...\",\"user_id\":\"usr_42\"}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/IngestionAccepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/bulk-log": {
      "post": {
        "operationId": "bulkLogEvents",
        "summary": "Log up to 50 events",
        "description": "Appends arrays of serialized JSON objects and corresponding Unix-second timestamps to one collection.",
        "tags": [
          "Ingestion"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkLogRequest"
              },
              "examples": {
                "orders": {
                  "value": {
                    "api_key": "your_api_key",
                    "collection": "orders",
                    "timestamps": [
                      1785081600,
                      1785081612
                    ],
                    "jsons": [
                      "{\"event\":\"order_completed\",\"event_id\":\"evt_1\",\"amount\":4900}",
                      "{\"event\":\"order_completed\",\"event_id\":\"evt_2\",\"amount\":7900}"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/IngestionAccepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/visualize/data": {
      "post": {
        "operationId": "getVisualizationData",
        "summary": "Get visualization data",
        "description": "Returns Samples, time-series, aggregate, comparison, or SQL visualization data.",
        "tags": [
          "Visualization"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VisualizationRequest"
              },
              "examples": {
                "dailySignups": {
                  "value": {
                    "api_key": "your_api_key",
                    "collection": "product_events",
                    "IANA_time_zone": "UTC",
                    "graph_type": "Single Line",
                    "start": "30 days ago",
                    "end": "now",
                    "filters": [
                      [
                        "event",
                        "=",
                        "signup_completed"
                      ]
                    ],
                    "aggregation": "Count",
                    "metric": null,
                    "split": null,
                    "granularity": "Day",
                    "customizations": {}
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Visualization result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataResponse"
                },
                "examples": {
                  "timeSeries": {
                    "summary": "Daily count with comparison",
                    "value": {
                      "result": [
                        {
                          "timestamp": "2026-07-24",
                          "Count": 83
                        },
                        {
                          "timestamp": "2026-07-25",
                          "Count": 91
                        }
                      ],
                      "compare_result": [
                        {
                          "timestamp": "2026-07-24",
                          "Count": 72
                        },
                        {
                          "timestamp": "2026-07-25",
                          "Count": 80
                        }
                      ],
                      "metadata": {
                        "granularity": "Day",
                        "combined": 174,
                        "compare_combined": 152
                      }
                    }
                  },
                  "samples": {
                    "summary": "Raw sample events",
                    "value": {
                      "result": [
                        {
                          "timestamp": 1785081600,
                          "json": {
                            "event": "signup_completed",
                            "user_id": "usr_42"
                          }
                        }
                      ]
                    }
                  },
                  "categories": {
                    "summary": "Categorical aggregate",
                    "value": {
                      "result": [
                        {
                          "label": "pro",
                          "value": 184
                        },
                        {
                          "label": "starter",
                          "value": 96
                        }
                      ],
                      "metadata": {}
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/api/visualize/iframe": {
      "post": {
        "operationId": "generateIframeUrl",
        "summary": "Generate an iframe URL",
        "description": "Returns an opaque bearer URL for the requested visualization configuration.",
        "tags": [
          "Visualization"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VisualizationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated iframe URL.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IframeResponse"
                },
                "example": {
                  "url": "https://graphjson.com/embed?p=..."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/api/visualize/embed": {
      "post": {
        "operationId": "generateEmbedUrls",
        "summary": "Generate iframe and image URLs",
        "description": "Returns opaque bearer URLs for an iframe and raster image representation.",
        "tags": [
          "Visualization"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VisualizationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Generated embed URLs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmbedResponse"
                },
                "example": {
                  "iframe_url": "https://graphjson.com/embed?p=...",
                  "image_url": "https://graphjson.com/api/image?p=..."
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ApiKey": {
        "type": "string",
        "minLength": 1,
        "description": "Secret workspace API key. Keep server-side."
      },
      "CollectionName": {
        "type": "string",
        "minLength": 1,
        "examples": [
          "product_events"
        ]
      },
      "UnixTimestamp": {
        "type": "integer",
        "description": "Whole Unix seconds.",
        "examples": [
          1785081600
        ]
      },
      "SerializedEvent": {
        "type": "string",
        "minLength": 2,
        "maxLength": 10000,
        "description": "A serialized JSON object. Top-level arrays are not accepted.",
        "examples": [
          "{\"event\":\"signup_completed\",\"event_id\":\"evt_01J...\"}"
        ]
      },
      "LogEventRequest": {
        "type": "object",
        "required": [
          "api_key",
          "json"
        ],
        "properties": {
          "api_key": {
            "$ref": "#/components/schemas/ApiKey"
          },
          "collection": {
            "$ref": "#/components/schemas/CollectionName"
          },
          "timestamp": {
            "$ref": "#/components/schemas/UnixTimestamp"
          },
          "json": {
            "$ref": "#/components/schemas/SerializedEvent"
          },
          "no_flatten": {
            "type": "boolean",
            "default": false,
            "description": "Preserve nested JSON when true."
          }
        },
        "additionalProperties": false
      },
      "BulkLogRequest": {
        "type": "object",
        "required": [
          "api_key",
          "timestamps",
          "jsons"
        ],
        "properties": {
          "api_key": {
            "$ref": "#/components/schemas/ApiKey"
          },
          "collection": {
            "$ref": "#/components/schemas/CollectionName"
          },
          "timestamps": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/UnixTimestamp"
            },
            "description": "Must contain one entry for every item in jsons."
          },
          "jsons": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/SerializedEvent"
            },
            "description": "Must contain one entry for every item in timestamps."
          },
          "no_flatten": {
            "type": "boolean",
            "default": false,
            "description": "Preserve nested JSON when true."
          }
        },
        "additionalProperties": false
      },
      "FilterCondition": {
        "type": "array",
        "prefixItems": [
          {
            "type": "string",
            "description": "Field name."
          },
          {
            "type": "string",
            "enum": [
              "=",
              "≠",
              "includes",
              "excludes",
              ">",
              "<"
            ]
          },
          {
            "description": "Comparison value.",
            "type": [
              "string",
              "number",
              "boolean"
            ]
          }
        ],
        "minItems": 3,
        "maxItems": 3
      },
      "FilterList": {
        "type": "array",
        "description": "A flat AND list of conditions or nested OR groups of AND conditions.",
        "items": {
          "oneOf": [
            {
              "$ref": "#/components/schemas/FilterCondition"
            },
            {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/FilterCondition"
              }
            }
          ]
        }
      },
      "VisualizationRequest": {
        "type": "object",
        "required": [
          "api_key",
          "graph_type"
        ],
        "anyOf": [
          {
            "title": "Collection visualizer request",
            "required": [
              "start",
              "end"
            ]
          },
          {
            "title": "SQL visualization request",
            "required": [
              "sql_query"
            ]
          }
        ],
        "properties": {
          "api_key": {
            "$ref": "#/components/schemas/ApiKey"
          },
          "collection": {
            "$ref": "#/components/schemas/CollectionName"
          },
          "IANA_time_zone": {
            "type": "string",
            "default": "America/Los_Angeles",
            "examples": [
              "UTC",
              "America/Los_Angeles"
            ]
          },
          "graph_type": {
            "type": "string",
            "enum": [
              "Samples",
              "Single Line",
              "Multi Line",
              "Stacked Line",
              "Cumulative Line",
              "Ratio Line",
              "Compare Line",
              "Single Value",
              "Table",
              "Bar Chart",
              "Pie Chart",
              "Funnel"
            ]
          },
          "start": {
            "oneOf": [
              {
                "type": "string",
                "examples": [
                  "30 days ago"
                ]
              },
              {
                "$ref": "#/components/schemas/UnixTimestamp"
              }
            ]
          },
          "end": {
            "oneOf": [
              {
                "type": "string",
                "examples": [
                  "now"
                ]
              },
              {
                "$ref": "#/components/schemas/UnixTimestamp"
              }
            ]
          },
          "filters": {
            "$ref": "#/components/schemas/FilterList"
          },
          "compare_filters": {
            "$ref": "#/components/schemas/FilterList"
          },
          "numerator_filters": {
            "$ref": "#/components/schemas/FilterList"
          },
          "denominator_filters": {
            "$ref": "#/components/schemas/FilterList"
          },
          "aggregation": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "Count",
              "Sum",
              "Avg"
            ]
          },
          "metric": {
            "type": [
              "string",
              "null"
            ]
          },
          "split": {
            "type": [
              "string",
              "null"
            ]
          },
          "granularity": {
            "type": "string",
            "enum": [
              "Auto",
              "Minute",
              "Hour",
              "Day",
              "Week",
              "Month"
            ]
          },
          "compare": {
            "type": [
              "string",
              "null"
            ],
            "examples": [
              "30 days ago"
            ]
          },
          "order": {
            "type": "string",
            "enum": [
              "Ascending",
              "Descending"
            ]
          },
          "customizations": {
            "type": "object",
            "additionalProperties": true
          },
          "sql_query": {
            "type": "string",
            "description": "Read-only saved-style SQL query."
          },
          "timestamp_column": {
            "type": "string"
          },
          "value_column": {
            "type": "string"
          },
          "split_column": {
            "type": "string"
          },
          "label_column": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "DataResponse": {
        "type": "object",
        "description": "Stable Data API envelope. The row shape depends on the requested graph type and SQL column mapping; see the named row schemas and the Data API response-contract guide.",
        "required": [
          "result"
        ],
        "properties": {
          "result": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/SampleRow"
                },
                {
                  "$ref": "#/components/schemas/TimeSeriesRow"
                },
                {
                  "$ref": "#/components/schemas/LabelValueRow"
                },
                {
                  "$ref": "#/components/schemas/TableRow"
                }
              ]
            }
          },
          "compare_result": {
            "type": "array",
            "description": "Comparison rows when the request uses a comparison graph or comparison window.",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/TimeSeriesRow"
                },
                {
                  "$ref": "#/components/schemas/LabelValueRow"
                },
                {
                  "$ref": "#/components/schemas/TableRow"
                }
              ]
            }
          },
          "metadata": {
            "type": "object",
            "description": "Query-derived totals, resolved granularity, and other shape-specific metadata.",
            "additionalProperties": true
          },
          "telemetry": {
            "type": "object",
            "description": "Optional query execution telemetry. Treat additional telemetry fields as additive.",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "SampleRow": {
        "type": "object",
        "description": "One Samples result with the original event time and parsed JSON payload.",
        "required": [
          "timestamp",
          "json"
        ],
        "properties": {
          "timestamp": {
            "type": "number",
            "description": "Event time as a Unix second."
          },
          "json": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": true
      },
      "TimeSeriesRow": {
        "type": "object",
        "description": "One resolved time bucket. Numeric value keys depend on the aggregation, metric, and split.",
        "required": [
          "timestamp"
        ],
        "properties": {
          "timestamp": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              }
            ]
          }
        },
        "additionalProperties": true
      },
      "LabelValueRow": {
        "type": "object",
        "description": "One label/value aggregate used by categorical and mapped SQL visualizations.",
        "required": [
          "label",
          "value"
        ],
        "properties": {
          "label": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "value": {
            "type": "number"
          }
        },
        "additionalProperties": true
      },
      "TableRow": {
        "type": "object",
        "description": "A table or SQL row whose keys come from the selected fields or query aliases.",
        "additionalProperties": true
      },
      "IframeResponse": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Opaque bearer URL for the generated iframe."
          }
        },
        "additionalProperties": true
      },
      "EmbedResponse": {
        "type": "object",
        "required": [
          "iframe_url",
          "image_url"
        ],
        "properties": {
          "iframe_url": {
            "type": "string",
            "format": "uri",
            "description": "Opaque bearer URL for the generated iframe."
          },
          "image_url": {
            "type": "string",
            "format": "uri",
            "description": "Opaque bearer URL for the generated image."
          }
        },
        "additionalProperties": true
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "description": "Human-readable error. Wording can evolve.",
            "type": [
              "string",
              "object",
              "array",
              "null"
            ]
          }
        },
        "additionalProperties": true
      }
    },
    "responses": {
      "IngestionAccepted": {
        "description": "Request accepted. Do not depend on undocumented success-body fields.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid method, key, payload, timestamp, range, filter, or query.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Invalid API KEY"
            }
          },
          "text/plain": {
            "schema": {
              "type": "string"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Ingestion request guardrail exceeded. Retry with exponential backoff and jitter.",
        "content": {
          "text/plain": {
            "schema": {
              "type": "string"
            },
            "example": "Too Many Requests"
          },
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "InternalError": {
        "description": "Internal ingestion failure. Retry a bounded number of times.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          },
          "text/plain": {
            "schema": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}
