{
  "openapi": "3.1.0",
  "info": {
    "title": "JustLog3 Cloud Log Ingestion API",
    "version": "1.0.0",
    "description": "Send plain-text log lines; every line of the body is one log line. Guide: https://jl3-cloud.site/docs/http-api/"
  },
  "servers": [
    {
      "url": "https://jl3-cloud.site"
    }
  ],
  "security": [
    {
      "bearerToken": []
    }
  ],
  "paths": {
    "/api/logs/": {
      "post": {
        "operationId": "sendLogs",
        "summary": "Send a batch of log lines",
        "parameters": [
          {
            "name": "Content-Encoding",
            "in": "header",
            "required": false,
            "description": "Compressed body.",
            "schema": {
              "type": "string",
              "enum": [
                "gzip",
                "deflate",
                "zstd"
              ]
            }
          },
          {
            "name": "X-JustLog-Logger",
            "in": "header",
            "required": false,
            "description": "Source name shown in the dashboard. Default \"unknown\".",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "name": "X-JustLog-Session",
            "in": "header",
            "required": false,
            "description": "Groups the requests of one run.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          },
          {
            "name": "X-JustLog-Seq",
            "in": "header",
            "required": false,
            "description": "Orders a session. A repeat of the same session and Seq within 30 minutes is stored once.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 2147483647
            }
          },
          {
            "name": "X-JustLog-UTC-Offset",
            "in": "header",
            "required": false,
            "description": "UTC offset of the client in seconds east of UTC, e.g. 18000 for UTC+5.",
            "schema": {
              "type": "integer",
              "minimum": -50400,
              "maximum": 50400
            }
          }
        ],
        "requestBody": {
          "required": true,
          "description": "Log lines separated by newlines. Lines over 8192 characters are cut.",
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Stored, fully or up to the daily quota; or a repeat that was already stored.",
            "headers": {
              "X-JustLog-Lines-Accepted": {
                "description": "Lines stored from this batch.",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-JustLog-Lines-Rejected": {
                "description": "Lines over the daily quota (only when non-zero).",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-JustLog-Lines-Remaining": {
                "description": "Lines left today (plans with a limit).",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "X-JustLog-Repeat": {
                "description": "\"1\" when this session and Seq were already stored.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Not text/plain, an empty body or a corrupt compressed body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "e1": {
                    "value": {
                      "error": "Request body must be plain text."
                    }
                  },
                  "e2": {
                    "value": {
                      "error": "Log payload is empty."
                    }
                  },
                  "e3": {
                    "value": {
                      "error": "The compressed body is corrupt."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, wrong or rotated token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "e1": {
                    "value": {
                      "error": "Bearer token required."
                    }
                  },
                  "e2": {
                    "value": {
                      "error": "Invalid or expired token."
                    }
                  }
                }
              }
            }
          },
          "405": {
            "description": "Only POST is allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "e1": {
                    "value": {
                      "error": "Only POST requests are allowed."
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Over 1 MB as sent or 4 MB after decoding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "e1": {
                    "value": {
                      "error": "Batch is too large (max 1 MB as sent and 4 MB of text). Send smaller batches."
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unknown Content-Encoding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Over 120 requests a minute for this token (Retry-After: 60), or the daily line limit is spent (Retry-After until 00:00 UTC; the body adds limit, retry_after and plans_url).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "The API token from the dashboard."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "limit": {
            "type": "integer"
          },
          "retry_after": {
            "type": "integer"
          },
          "plans_url": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    }
  }
}