{
  "openapi": "3.0.3",
  "info": {
    "title": "Find the Act Agency API",
    "version": "1.0.0",
    "description": "Manage your acts, availability and enquiries on Find the Act. Auth: `Authorization: Bearer fta_live_…` (create keys in the agency dashboard → API). Rate limit 120 requests/minute per key. Write requests accept an `Idempotency-Key` header (replayed for 24h)."
  },
  "servers": [
    {
      "url": "https://findtheact.co.uk/api/v1"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "summary": "Who am I (agency, key scopes)",
        "description": "",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/categories": {
      "get": {
        "summary": "List act category slugs",
        "description": "",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/acts": {
      "get": {
        "summary": "List your acts",
        "description": "Requires scope `acts:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/acts/{ref}": {
      "parameters": [
        {
          "name": "ref",
          "in": "path",
          "required": true,
          "description": "Your external_ref for the act (or our numeric act id)",
          "schema": {
            "type": "string",
            "pattern": "^[\\w.-]{1,80}$"
          }
        }
      ],
      "get": {
        "summary": "Get one act",
        "description": "Requires scope `acts:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "summary": "Create or update an act (upsert by external_ref)",
        "description": "Creates the act if the external_ref is new (category required); otherwise updates the supplied fields. `media`, if supplied, replaces the act's media list. Requires scope `acts:write`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Idem"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActInput"
              }
            }
          }
        }
      }
    },
    "/acts/{ref}/availability": {
      "parameters": [
        {
          "name": "ref",
          "in": "path",
          "required": true,
          "description": "Your external_ref for the act (or our numeric act id)",
          "schema": {
            "type": "string",
            "pattern": "^[\\w.-]{1,80}$"
          }
        }
      ],
      "get": {
        "summary": "List upcoming dates",
        "description": "Requires scope `acts:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "summary": "Bulk upsert dates (optionally replace all API-sourced dates)",
        "description": "Requires scope `availability:write`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Idem"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AvailabilityInput"
              }
            }
          }
        }
      },
      "post": {
        "summary": "Bulk upsert dates",
        "description": "Requires scope `availability:write`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Idem"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AvailabilityInput"
              }
            }
          }
        }
      }
    },
    "/enquiries": {
      "get": {
        "summary": "List enquiries",
        "description": "Requires scope `enquiries:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "contacted",
                "quoted",
                "booked",
                "lost"
              ]
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        ]
      }
    },
    "/enquiries/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "summary": "Get one enquiry",
        "description": "Requires scope `enquiries:read`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "patch": {
        "summary": "Update stage and/or notes",
        "description": "Requires scope `enquiries:write`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Idem"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnquiryPatch"
              }
            }
          }
        }
      }
    },
    "/webhooks": {
      "get": {
        "summary": "List webhooks",
        "description": "Requires scope `webhooks:manage`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "summary": "Create a webhook (returns signing secret once)",
        "description": "Requires scope `webhooks:manage`.",
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/Idem"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookInput"
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "delete": {
        "summary": "Delete a webhook",
        "description": "Requires scope `webhooks:manage`.",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/webhooks/{id}/test": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "summary": "Queue a ping delivery",
        "description": "Requires scope `webhooks:manage`.",
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "fta_live_<prefix>_<secret>"
      }
    },
    "parameters": {
      "Idem": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "pattern": "^[\\w.:-]{8,100}$"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {}
            }
          }
        }
      },
      "ActInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "category": {
            "type": "string",
            "description": "slug from /categories; required on create"
          },
          "tagline": {
            "type": "string",
            "maxLength": 160
          },
          "bio": {
            "type": "string",
            "maxLength": 6000
          },
          "town": {
            "type": "string"
          },
          "travels_nationwide": {
            "type": "boolean"
          },
          "price_from": {
            "type": "integer",
            "description": "GBP"
          },
          "price_to": {
            "type": "integer"
          },
          "media": {
            "type": "array",
            "maxItems": 30,
            "items": {
              "type": "object",
              "required": [
                "kind",
                "url"
              ],
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "image",
                    "video",
                    "audio"
                  ]
                },
                "url": {
                  "type": "string",
                  "format": "uri",
                  "description": "https only; YouTube/Vimeo for video"
                },
                "caption": {
                  "type": "string"
                }
              }
            }
          },
          "cancellation_policy": {
            "type": "string"
          },
          "substitution_policy": {
            "type": "string"
          },
          "deposit_terms": {
            "type": "string"
          },
          "ical_url": {
            "type": "string",
            "format": "uri",
            "description": "Alternative to pushing availability: we import booked dates from this feed every 6 hours"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "hidden"
            ]
          }
        }
      },
      "AvailabilityInput": {
        "type": "object",
        "required": [
          "dates"
        ],
        "properties": {
          "replace": {
            "type": "boolean",
            "description": "PUT only: delete your previously pushed future dates first"
          },
          "dates": {
            "type": "array",
            "maxItems": 1000,
            "items": {
              "type": "object",
              "required": [
                "date",
                "status"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "free",
                    "booked"
                  ]
                }
              }
            }
          }
        }
      },
      "EnquiryPatch": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "new",
              "contacted",
              "quoted",
              "booked",
              "lost"
            ]
          },
          "notes": {
            "type": "string",
            "maxLength": 4000
          }
        }
      },
      "WebhookInput": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "https only"
          }
        }
      }
    }
  },
  "x-webhooks": {
    "enquiry.created": {
      "description": "POSTed to your URL when a booker enquires about one of your acts. Headers: X-FTA-Event, X-FTA-Delivery, X-FTA-Signature: t=<unix>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}` using your whsec_ secret>. Respond 2xx within 8s. Retries after 1, 5, 30, 120, 720 and 1440 minutes."
    }
  }
}