{
  "openapi": "3.1.0",
  "info": {
    "title": "TAYO.pro API",
    "version": "1.0.0",
    "description": "The TAYO.pro public API. Authenticate with a Bearer key: a `tay_live_*` key operates on your real data, a `tay_test_*` key runs the same authentication and the same request validation and then returns a fixture without touching anything."
  },
  "servers": [
    {
      "url": "https://staging.tayo.pro/api/v1",
      "description": "Staging"
    }
  ],
  "tags": [
    {
      "name": "Service"
    },
    {
      "name": "Webhooks"
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "me",
        "summary": "Identify the company and mode of the presented API key",
        "tags": [
          "Service"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "400": {
            "description": "The request failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "The API key may not perform this action.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error occurred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/ping": {
      "get": {
        "operationId": "ping",
        "summary": "Check that the API is reachable",
        "tags": [
          "Service"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ping"
                }
              }
            }
          },
          "400": {
            "description": "The request failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error occurred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/test/webhooks/send": {
      "post": {
        "operationId": "test.webhooks.send",
        "summary": "Send a test webhook event to one of your test-mode endpoints",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$schema": "https://json-schema.org/draft/2020-12/schema",
                "type": "object",
                "properties": {
                  "endpointId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "event": {
                    "type": "string",
                    "enum": [
                      "projectCreated",
                      "projectUpdated",
                      "projectShared",
                      "projectClientValidationRequested",
                      "projectAccepted",
                      "projectDeclined",
                      "projectCanceled",
                      "projectDone",
                      "projectRated",
                      "projectReminderCreated",
                      "projectReminderUpdated",
                      "projectReminderDeleted",
                      "projectReminderSent",
                      "jobCreated",
                      "jobUpdated",
                      "jobScheduled",
                      "jobRescheduled",
                      "jobDone",
                      "jobNoShow",
                      "jobCanceled",
                      "messageCreated",
                      "quoteCreated",
                      "quoteUpdated",
                      "quoteDeleted",
                      "quoteSent",
                      "quoteAccepted",
                      "quoteDeclined",
                      "invoiceCreated",
                      "invoiceUpdated",
                      "invoiceDeleted",
                      "invoiceSent",
                      "invoiceAccepted",
                      "invoiceDeclined",
                      "invoiceAutoMarkPaidFailed",
                      "invoiceReminderCreated",
                      "invoiceReminderUpdated",
                      "invoiceReminderDeleted",
                      "invoiceReminderSent",
                      "invoiceReminderFailed"
                    ]
                  }
                },
                "required": [
                  "endpointId",
                  "event"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestSend"
                }
              }
            }
          },
          "400": {
            "description": "The request failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "The API key may not perform this action.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error occurred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "usage",
        "summary": "Report the current month's API usage and the limits in force",
        "tags": [
          "Service"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "400": {
            "description": "The request failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "The API key may not perform this action.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error occurred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/webhook-deliveries/{deliveryId}/retry": {
      "post": {
        "operationId": "webhookDeliveries.retry",
        "summary": "Re-queue a webhook delivery, keeping the same event id",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "deliveryId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 200,
              "pattern": "^[A-Za-z0-9_-]+$"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDeliveryRetry"
                }
              }
            }
          },
          "400": {
            "description": "The request failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "The API key is missing, malformed or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "The API key may not perform this action.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit or quota exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "500": {
            "description": "An unexpected error occurred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your API key, sent as `Authorization: Bearer tay_live_…`."
      }
    },
    "schemas": {
      "ApiError": {
        "description": "The error envelope returned by every failing request.",
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ApiErrorDetail"
                }
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "ApiErrorDetail": {
        "description": "One field-level problem with a request.",
        "type": "object",
        "properties": {
          "field": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        },
        "required": [
          "field",
          "code",
          "message"
        ],
        "additionalProperties": false
      },
      "Me": {
        "description": "The company and mode the presented API key resolves to.",
        "type": "object",
        "properties": {
          "companyId": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          }
        },
        "required": [
          "companyId",
          "mode"
        ],
        "additionalProperties": false
      },
      "Ping": {
        "description": "Liveness probe response.",
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "const": "ok"
          },
          "version": {
            "type": "string",
            "const": "v1"
          }
        },
        "required": [
          "status",
          "version"
        ],
        "additionalProperties": false
      },
      "Usage": {
        "description": "Current month's API usage for the presented key's mode.",
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "period": {
            "type": "string"
          },
          "resetAt": {
            "type": "string"
          },
          "limits": {
            "type": "object",
            "properties": {
              "monthlyRequests": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "burstPerMinute": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "source": {
                "type": "string",
                "enum": [
                  "default",
                  "override"
                ]
              }
            },
            "required": [
              "monthlyRequests",
              "burstPerMinute",
              "source"
            ],
            "additionalProperties": false
          },
          "month": {
            "type": "object",
            "properties": {
              "used": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "remaining": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "state": {
                "type": "string",
                "enum": [
                  "within",
                  "overage",
                  "exhausted"
                ]
              }
            },
            "required": [
              "used",
              "remaining",
              "state"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "mode",
          "period",
          "resetAt",
          "limits",
          "month"
        ],
        "additionalProperties": false
      },
      "WebhookDeliveryRetry": {
        "description": "A webhook delivery that has been re-queued for one more attempt.",
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "const": "pending"
          },
          "attemptCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "manualRetryCount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "manualRetriesRemaining": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "queuedAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
          }
        },
        "required": [
          "id",
          "status",
          "attemptCount",
          "manualRetryCount",
          "manualRetriesRemaining",
          "queuedAt"
        ],
        "additionalProperties": false
      },
      "WebhookTestSend": {
        "description": "A webhook test event that has been queued for delivery.",
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string"
          },
          "endpointId": {
            "type": "string"
          },
          "event": {
            "type": "string",
            "enum": [
              "projectCreated",
              "projectUpdated",
              "projectShared",
              "projectClientValidationRequested",
              "projectAccepted",
              "projectDeclined",
              "projectCanceled",
              "projectDone",
              "projectRated",
              "projectReminderCreated",
              "projectReminderUpdated",
              "projectReminderDeleted",
              "projectReminderSent",
              "jobCreated",
              "jobUpdated",
              "jobScheduled",
              "jobRescheduled",
              "jobDone",
              "jobNoShow",
              "jobCanceled",
              "messageCreated",
              "quoteCreated",
              "quoteUpdated",
              "quoteDeleted",
              "quoteSent",
              "quoteAccepted",
              "quoteDeclined",
              "invoiceCreated",
              "invoiceUpdated",
              "invoiceDeleted",
              "invoiceSent",
              "invoiceAccepted",
              "invoiceDeclined",
              "invoiceAutoMarkPaidFailed",
              "invoiceReminderCreated",
              "invoiceReminderUpdated",
              "invoiceReminderDeleted",
              "invoiceReminderSent",
              "invoiceReminderFailed"
            ]
          },
          "status": {
            "type": "string",
            "const": "pending"
          }
        },
        "required": [
          "eventId",
          "endpointId",
          "event",
          "status"
        ],
        "additionalProperties": false
      }
    }
  }
}
