{
  "openapi": "3.1.0",
  "info": {
    "title": "Taddoo public website API",
    "version": "2.0.0",
    "description": "Connect a tattoo artist's or studio's own website to Taddoo, or rebuild the whole Taddoo page. API 2.0 is additive: paths stay under /api/v1 and existing integrations keep working. Full guide for coding agents: https://taddoo.com/developers/llms.txt",
    "contact": {
      "name": "Taddoo AG",
      "email": "hello@tattoomii.com",
      "url": "https://taddoo.com/developers"
    },
    "termsOfService": "https://taddoo.com/agb"
  },
  "servers": [
    {
      "url": "https://taddoo.com/api/v1"
    }
  ],
  "security": [
    {
      "bearer": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "Publishable key taddoo_pk_... from the Taddoo app (safe in browser code)."
      }
    },
    "parameters": {
      "artist": {
        "name": "artist",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Only with a studio key: slug of a linked artist (from GET /studio)."
      },
      "locale": {
        "name": "locale",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "minLength": 2,
          "maxLength": 2
        },
        "description": "Language of texts and messages; default Accept-Language, then the artist's language."
      }
    },
    "responses": {
      "Error": {
        "description": "Error. Show error.message to visitors when code is 'rejected' (already translated).",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "retryAfterSec": {
                      "type": "integer"
                    },
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Form requests (422): ids of the missing questions"
                    },
                    "variantId": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "available": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "code",
                    "message"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Theme": {
        "type": "object",
        "description": "Page design. preset, accent, background, mode, font, socialInHeader, subpageHeader exist since v1; the rest is API 2.0.",
        "properties": {
          "preset": {
            "type": "string"
          },
          "accent": {
            "type": "string"
          },
          "background": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "light",
              "dark"
            ]
          },
          "font": {
            "type": "string"
          },
          "socialInHeader": {
            "type": "boolean"
          },
          "subpageHeader": {
            "type": "boolean"
          },
          "textFont": {
            "type": [
              "string",
              "null"
            ]
          },
          "uppercaseHeadings": {
            "type": "boolean"
          },
          "pattern": {
            "type": "string",
            "enum": [
              "none",
              "dots",
              "grid",
              "noise",
              "paper",
              "stripes",
              "checker",
              "scanlines",
              "halftone",
              "waves",
              "sunburst",
              "zigzag"
            ]
          },
          "radius": {
            "type": "string",
            "enum": [
              "round",
              "square"
            ]
          },
          "layout": {
            "type": "string",
            "enum": [
              "grid",
              "masonry",
              "large"
            ]
          },
          "header": {
            "type": "string",
            "enum": [
              "avatar",
              "cover",
              "text"
            ]
          },
          "backgroundImageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "backgroundImageOpacity": {
            "type": "number",
            "minimum": 0.1,
            "maximum": 1
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "logoSize": {
            "type": "string",
            "enum": [
              "s",
              "m",
              "l"
            ]
          },
          "showName": {
            "type": "boolean"
          },
          "button": {
            "type": "object",
            "properties": {
              "fill": {
                "type": "string",
                "enum": [
                  "contrast",
                  "accent",
                  "outline",
                  "card",
                  "light"
                ]
              },
              "palette": {
                "type": "string",
                "enum": [
                  "none",
                  "rainbow",
                  "pastel",
                  "sunset",
                  "ocean",
                  "forest",
                  "mono"
                ]
              },
              "paletteColors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "images": {
                "type": "string",
                "enum": [
                  "alternate",
                  "left",
                  "right"
                ]
              },
              "shape": {
                "type": "string",
                "enum": [
                  "pill",
                  "round",
                  "square"
                ]
              },
              "border": {
                "type": "string",
                "enum": [
                  "none",
                  "thin",
                  "thick",
                  "double",
                  "dashed"
                ]
              },
              "text": {
                "type": "string",
                "enum": [
                  "auto",
                  "normal",
                  "upper",
                  "spaced"
                ]
              },
              "deco": {
                "type": "string",
                "enum": [
                  "none",
                  "stars",
                  "sparkle",
                  "tilde",
                  "dots",
                  "hearts",
                  "arrow"
                ]
              },
              "textColor": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "shadow": {
            "type": "string",
            "enum": [
              "none",
              "soft",
              "hard",
              "glow"
            ]
          },
          "avatarShape": {
            "type": "string",
            "enum": [
              "circle",
              "rounded",
              "square"
            ]
          },
          "align": {
            "type": "string",
            "enum": [
              "center",
              "left"
            ]
          },
          "entrance": {
            "type": "string",
            "enum": [
              "none",
              "fade",
              "slide",
              "pop",
              "bounce"
            ]
          },
          "primaryEffect": {
            "type": "string",
            "enum": [
              "none",
              "shine",
              "pulse",
              "wiggle",
              "glow"
            ]
          },
          "social": {
            "type": "object",
            "properties": {
              "style": {
                "type": "string",
                "enum": [
                  "circle",
                  "plain",
                  "filled"
                ]
              },
              "place": {
                "type": "string",
                "enum": [
                  "list",
                  "header",
                  "footer"
                ]
              }
            }
          },
          "followPopup": {
            "type": "boolean"
          },
          "showTaken": {
            "type": "boolean"
          },
          "hideBranding": {
            "type": "boolean",
            "description": "Only on the main page theme"
          },
          "cssVariables": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Derived colours as CSS custom properties (--fl-bg, --fl-fg, --fl-btn-*, ...)"
          }
        }
      },
      "Block": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "link",
              "text",
              "social",
              "heading",
              "address",
              "form",
              "flash",
              "availability",
              "waitlist",
              "voucher",
              "shop",
              "consultation",
              "touchup",
              "treatment"
            ]
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "type": [
              "string",
              "null"
            ]
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Address blocks: the address text."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "Voucher blocks: currency of the amounts."
          },
          "handles": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          "formId": {
            "type": "string"
          },
          "amounts": {
            "type": "array",
            "items": {
              "type": "number"
            }
          },
          "customAmount": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "min": {
                    "type": "number"
                  },
                  "max": {
                    "type": "number"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "treatmentId": {
            "type": "string",
            "description": "treatment blocks: service id"
          },
          "api": {
            "type": "string",
            "description": "Endpoint that serves the block's content"
          },
          "visible": {
            "type": "boolean",
            "description": "2.0: shown as its own element in the list right now"
          },
          "inPrimary": {
            "type": "boolean",
            "description": "2.0: reached through the main button"
          },
          "paused": {
            "type": "boolean"
          },
          "placement": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "list",
              "header",
              "footer",
              null
            ]
          },
          "button": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "background": {
                    "type": "string"
                  },
                  "text": {
                    "type": "string"
                  },
                  "border": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "imageSide": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "left",
              "right",
              null
            ]
          },
          "theme": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Theme"
              },
              {
                "type": "null"
              }
            ]
          },
          "backgroundImageUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "visibleFrom": {
            "type": [
              "string",
              "null"
            ]
          },
          "visibleUntil": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Primary": {
        "type": "object",
        "description": "Main button: no ways = no button, one way = direct, several = chooser with question",
        "properties": {
          "label": {
            "type": "string"
          },
          "customLabel": {
            "type": [
              "string",
              "null"
            ]
          },
          "question": {
            "type": "string"
          },
          "ways": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "form",
                    "instagram",
                    "treatment",
                    "flash",
                    "consultation"
                  ]
                },
                "blockId": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "subtitle": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "url": {
                  "type": "string"
                },
                "external": {
                  "type": "boolean"
                },
                "api": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "treatmentId": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "SmsOptIn": {
        "type": "object",
        "properties": {
          "offered": {
            "type": "boolean"
          },
          "channels": {
            "type": "object",
            "properties": {
              "sms": {
                "type": "boolean"
              },
              "whatsapp": {
                "type": "boolean"
              }
            }
          },
          "showWhen": {
            "type": "string"
          },
          "label": {
            "type": "object",
            "properties": {
              "default": {
                "type": "string"
              },
              "us": {
                "type": "string"
              }
            }
          },
          "hint": {
            "type": "object",
            "properties": {
              "default": {
                "type": "string"
              },
              "us": {
                "type": "string"
              }
            }
          },
          "termsLabel": {
            "type": "string"
          },
          "termsUrl": {
            "type": "string"
          },
          "field": {
            "type": "string",
            "const": "smsConsent"
          }
        }
      },
      "ArtistPage": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "const": "artist"
          },
          "locale": {
            "type": "string",
            "description": "Language of the texts in this response."
          },
          "slug": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "headline": {
            "type": [
              "string",
              "null"
            ]
          },
          "bio": {
            "type": [
              "string",
              "null"
            ]
          },
          "instagram": {
            "type": [
              "string",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "avatarUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "coverUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "type": "string"
          },
          "sizeUnit": {
            "type": "string",
            "enum": [
              "cm",
              "in"
            ]
          },
          "books": {
            "type": "object"
          },
          "depositsOnline": {
            "type": "boolean"
          },
          "theme": {
            "$ref": "#/components/schemas/Theme"
          },
          "subpageThemes": {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/Theme"
                },
                {
                  "type": "null"
                }
              ]
            },
            "description": "flash, availability, waitlist, consultation, touchup, voucher, shop"
          },
          "hideBranding": {
            "type": "boolean"
          },
          "studio": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "slug": {
                    "type": "string"
                  },
                  "url": {
                    "type": "string"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "primary": {
            "$ref": "#/components/schemas/Primary"
          },
          "smsOptIn": {
            "$ref": "#/components/schemas/SmsOptIn"
          },
          "blocks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Block"
            }
          }
        }
      },
      "ConsultMode": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "in_person",
              "online",
              "phone",
              "whatsapp"
            ]
          },
          "label": {
            "type": "string"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ]
          },
          "homeStudioOnly": {
            "type": "boolean"
          },
          "provider": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "google_meet",
              "own_link",
              null
            ]
          },
          "whoCalls": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "artist",
              "customer",
              null
            ]
          },
          "artistPhone": {
            "type": [
              "string",
              "null"
            ]
          },
          "customerPhoneRequired": {
            "type": "boolean"
          }
        }
      },
      "ConsultationBlock": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "consultation",
              "touchup"
            ]
          },
          "title": {
            "type": "string"
          },
          "intro": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          },
          "paused": {
            "type": "boolean"
          },
          "pausedNote": {
            "type": [
              "string",
              "null"
            ]
          },
          "bookable": {
            "type": "boolean"
          },
          "options": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "durationMinutes": {
                  "type": "integer"
                },
                "price": {
                  "type": "number"
                },
                "free": {
                  "type": "boolean"
                },
                "currency": {
                  "type": "string"
                },
                "bookingToken": {
                  "type": "string"
                },
                "bookUrl": {
                  "type": "string"
                }
              }
            }
          },
          "modes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConsultMode"
            }
          },
          "customerPicksMode": {
            "type": "boolean"
          },
          "askDescription": {
            "type": "string",
            "enum": [
              "required",
              "optional",
              "off"
            ]
          },
          "askPhoto": {
            "type": "string",
            "enum": [
              "required",
              "optional",
              "off"
            ]
          },
          "maxPhotos": {
            "type": "integer"
          },
          "leadTime": {
            "type": "object",
            "properties": {
              "minLeadHours": {
                "type": "number"
              },
              "maxLeadDays": {
                "type": "number"
              }
            }
          }
        }
      },
      "BookingDetail": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "consultation",
              "touchup",
              "treatment"
            ]
          },
          "title": {
            "type": "string"
          },
          "artistName": {
            "type": "string"
          },
          "blockId": {
            "type": "string"
          },
          "treatment": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "category": {
                    "type": "string"
                  },
                  "offerId": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "durationMinutes": {
            "type": "integer"
          },
          "price": {
            "type": [
              "number",
              "null"
            ]
          },
          "currency": {
            "type": "string"
          },
          "free": {
            "type": "boolean"
          },
          "payment": {
            "type": "object",
            "properties": {
              "required": {
                "type": "boolean"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "deposit",
                  "full",
                  "none"
                ]
              },
              "amount": {
                "type": "number"
              },
              "fee": {
                "type": "number"
              },
              "total": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              },
              "chargeCurrency": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "chargeTotal": {
                "type": [
                  "number",
                  "null"
                ]
              }
            }
          },
          "modes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConsultMode"
            }
          },
          "customerPicksMode": {
            "type": "boolean"
          },
          "selectedMode": {
            "type": "string"
          },
          "fields": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string"
              },
              "phone": {
                "type": "string"
              },
              "instagram": {
                "type": "string"
              },
              "note": {
                "type": "string",
                "enum": [
                  "required",
                  "optional",
                  "off"
                ]
              },
              "noteLabel": {
                "type": "string"
              },
              "photos": {
                "type": "string",
                "enum": [
                  "required",
                  "optional",
                  "off"
                ]
              },
              "maxPhotos": {
                "type": "integer"
              },
              "smsConsent": {
                "type": "boolean"
              },
              "smsChannels": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "sms": {
                        "type": "boolean"
                      },
                      "whatsapp": {
                        "type": "boolean"
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          },
          "phoneCountry": {
            "type": [
              "string",
              "null"
            ]
          },
          "leadTime": {
            "type": "object",
            "properties": {
              "minLeadHours": {
                "type": "number"
              },
              "maxLeadDays": {
                "type": "number"
              }
            }
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "availableSlots": {
            "type": "integer"
          }
        }
      },
      "BookingSlots": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string"
          },
          "durationMinutes": {
            "type": "integer"
          },
          "mode": {
            "type": "string"
          },
          "slots": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "startAt": {
                  "type": "string"
                },
                "endAt": {
                  "type": "string"
                },
                "timezone": {
                  "type": "string"
                },
                "location": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "locationId": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "address": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "BookingResult": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "payment_required",
              "confirmed"
            ]
          },
          "checkoutUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "projectUrl": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "VoucherStatus": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "redeemed",
              "expired",
              "canceled",
              "refunded",
              "void"
            ]
          },
          "ready": {
            "type": "boolean"
          },
          "statusLabel": {
            "type": [
              "string",
              "null"
            ]
          },
          "code": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "balance": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "expiresAt": {
            "type": [
              "string",
              "null"
            ]
          },
          "recipientName": {
            "type": [
              "string",
              "null"
            ]
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "language": {
            "type": "string"
          },
          "artistName": {
            "type": "string"
          },
          "redemptions": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "pageUrl": {
            "type": "string"
          },
          "pdfUrl": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "RequestDays": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "acceptsRequests": {
            "type": "boolean"
          },
          "formId": {
            "type": [
              "string",
              "null"
            ]
          },
          "questionId": {
            "type": [
              "string",
              "null"
            ]
          },
          "locationQuestionId": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "dayParam": {
            "type": "string",
            "const": "day"
          }
        }
      }
    }
  },
  "paths": {
    "/me": {
      "get": {
        "summary": "Who the key belongs to",
        "responses": {
          "200": {
            "description": "Key owner",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/page": {
      "get": {
        "summary": "Artist page: profile, books status, full theme, subpage themes, main button (primary), smsOptIn and link-in-bio blocks",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Artist page: profile, books status, full theme, subpage themes, main button (primary), smsOptIn and link-in-bio blocks",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ArtistPage"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/flash": {
      "get": {
        "summary": "Flash designs with sizes, prices and deposits",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Flash designs with sizes, prices and deposits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/flash/{id}": {
      "get": {
        "summary": "One flash design with locations and booking fields",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Flash id"
          }
        ],
        "responses": {
          "200": {
            "description": "One flash design with locations and booking fields",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/flash/{id}/slots": {
      "get": {
        "summary": "Free start times for one size",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Flash id"
          },
          {
            "name": "variant",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "location",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "'home' or a location id"
          },
          {
            "name": "sessionId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,64}$"
            },
            "description": "Random id per visitor, 8 to 64 chars (letters, digits, - and _)"
          }
        ],
        "responses": {
          "200": {
            "description": "Free start times for one size",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/flash/{id}/hold": {
      "post": {
        "summary": "Hold a slot for 15 minutes",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Flash id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "variantId",
                  "startAt",
                  "endAt",
                  "sessionId"
                ],
                "properties": {
                  "variantId": {
                    "type": "string"
                  },
                  "startAt": {
                    "type": "string"
                  },
                  "endAt": {
                    "type": "string"
                  },
                  "sessionId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Held",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/flash/{id}/release": {
      "post": {
        "summary": "Release an own hold",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Flash id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sessionId"
                ],
                "properties": {
                  "sessionId": {
                    "type": "string"
                  },
                  "startAt": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Released",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/flash/{id}/book": {
      "post": {
        "summary": "Book a flash (bookingMode direct). Returns checkoutUrl when a deposit is due.",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Flash id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "variantId",
                  "startAt",
                  "sessionId",
                  "name",
                  "email"
                ],
                "properties": {
                  "variantId": {
                    "type": "string"
                  },
                  "startAt": {
                    "type": "string"
                  },
                  "sessionId": {
                    "type": "string"
                  },
                  "location": {
                    "type": "string"
                  },
                  "bodyPart": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string",
                    "description": "international format, e.g. +41791234567"
                  },
                  "instagram": {
                    "type": "string"
                  },
                  "birthdate": {
                    "type": "string",
                    "format": "date"
                  },
                  "note": {
                    "type": "string"
                  },
                  "custom": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "smsConsent": {
                    "type": "boolean",
                    "description": "API 2.0: SMS/WhatsApp checkbox ticked (only counts when the page offers it, see smsOptIn in GET /page)"
                  },
                  "returnUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where Stripe sends the visitor after the payment. Needs an allowed-websites list on the key with this origin on it (else 400 return_urls_need_allowed_origins / invalid_return_url); https (http only for localhost/127.0.0.1 when listed). Appended: taddoo_status=success&taddoo_session=<Stripe session id> (shop: taddoo_status=success&taddoo_order=<order token>)."
                  },
                  "cancelUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where the visitor lands after canceling in Stripe (through a signed taddoo.com link valid for 24 hours that releases held slots or stock). Same rules as returnUrl. Appended: taddoo_status=canceled (shop: plus taddoo_order). Default: returnUrl."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ status: payment_required | confirmed | reserved, checkoutUrl }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/flash/{id}/request": {
      "post": {
        "summary": "Request a flash (bookingMode request)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Flash id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "variantId",
                  "name",
                  "email"
                ],
                "properties": {
                  "variantId": {
                    "type": "string"
                  },
                  "location": {
                    "type": "string"
                  },
                  "bodyPart": {
                    "type": "string"
                  },
                  "wishDate": {
                    "type": "string",
                    "format": "date"
                  },
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string",
                    "description": "international format, e.g. +41791234567"
                  },
                  "instagram": {
                    "type": "string"
                  },
                  "birthdate": {
                    "type": "string",
                    "format": "date"
                  },
                  "note": {
                    "type": "string"
                  },
                  "custom": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ status: requested }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/forms": {
      "get": {
        "summary": "Request forms with their questions",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Request forms with their questions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/forms/{id}": {
      "get": {
        "summary": "One request form",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Form id"
          }
        ],
        "responses": {
          "200": {
            "description": "One request form",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/forms/{id}/submissions": {
      "post": {
        "summary": "Send a request (lands in the artist's app as a project). 422 rejected carries fields (missing question ids).",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Form id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "answers"
                ],
                "properties": {
                  "answers": {
                    "type": "object",
                    "description": "Keys: question ids (q_ prefix optional). Values: string, number, boolean (yes/no) or array.",
                    "additionalProperties": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "number"
                        },
                        {
                          "type": "boolean"
                        },
                        {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      ]
                    }
                  },
                  "treatmentId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "API 2.0: request about a service (treatmentId from GET /services); body part and size are then optional"
                  },
                  "offerId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "API 2.0: one of the service's offers[].id (only with treatmentId)"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "description": "q_<questionId> fields (repeat for several values), optional treatmentId and offerId, and image files in 'images'",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ status: received }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/availability": {
      "get": {
        "summary": "Books status, free days, guest spots, time off, booking window and requestDays (2.0)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Books status, free days, guest spots, time off, booking window and requestDays (2.0)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "books": {
                          "type": "object"
                        },
                        "timezone": {
                          "type": "string"
                        },
                        "horizonDays": {
                          "type": "integer"
                        },
                        "freeDays": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "freeTimes": {
                          "type": "object"
                        },
                        "studios": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "guestSpots": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "timeOff": {
                          "type": "array",
                          "items": {
                            "type": "object"
                          }
                        },
                        "windowEnd": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "windowOver": {
                          "type": "boolean"
                        },
                        "openFrom": {
                          "type": "string"
                        },
                        "requestDays": {
                          "$ref": "#/components/schemas/RequestDays"
                        }
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/waitlists": {
      "get": {
        "summary": "Open waitlists",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Open waitlists",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/waitlists/{id}/entries": {
      "post": {
        "summary": "Join a waitlist",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Waitlist id"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string",
                    "description": "international format, e.g. +41791234567"
                  },
                  "instagram": {
                    "type": "string"
                  },
                  "preferredContact": {
                    "type": "string",
                    "enum": [
                      "email",
                      "instagram",
                      "whatsapp",
                      "sms"
                    ]
                  },
                  "wish": {
                    "type": "string"
                  },
                  "bodyPart": {
                    "type": "string"
                  },
                  "size": {
                    "type": "number"
                  },
                  "sizeSecondary": {
                    "type": "number"
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "description": "Same fields as the JSON body (name, email, ...) plus image files in 'images'",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ status: joined }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/services": {
      "get": {
        "summary": "Fixed services (tattoo services, piercing, tooth gems, custom) with price type, duration, deposit, images, bookUrl or requestUrl; 2.0: treatmentId, offers[].bookingToken and bookingOptions[]",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Fixed services (tattoo services, piercing, tooth gems, custom) with price type, duration, deposit, images, bookUrl or requestUrl; 2.0: treatmentId, offers[].bookingToken and bookingOptions[]",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/consultation": {
      "get": {
        "summary": "API 2.0: the 'Book a consultation' block with options (bookingToken), modes and fields",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "API 2.0: the 'Book a consultation' block with options (bookingToken), modes and fields",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConsultationBlock"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/touchup": {
      "get": {
        "summary": "API 2.0: the 'Book a touch-up' block with options (bookingToken)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "API 2.0: the 'Book a touch-up' block with options (bookingToken)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConsultationBlock"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bookings/{token}": {
      "get": {
        "summary": "API 2.0: what is booked (consultation, touch-up or treatment): duration, payment incl. fee, modes, fields",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,64}$"
            },
            "description": "bookingToken from GET /consultation, /touchup or /services"
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "in_person",
                "online",
                "phone",
                "whatsapp"
              ]
            },
            "description": "Consultation format (default: the first offered)"
          },
          {
            "name": "sessionId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,64}$"
            },
            "description": "Random id per visitor, 8 to 64 chars (letters, digits, - and _)"
          }
        ],
        "responses": {
          "200": {
            "description": "Booking details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BookingDetail"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "summary": "API 2.0: book a time. Returns checkoutUrl when a payment is due (alias: POST /bookings/{token}/submit)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,64}$"
            },
            "description": "bookingToken from GET /consultation, /touchup or /services"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "startAt",
                  "sessionId",
                  "name",
                  "email"
                ],
                "properties": {
                  "startAt": {
                    "type": "string"
                  },
                  "sessionId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string",
                    "description": "international format, e.g. +41791234567"
                  },
                  "instagram": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "in_person",
                      "online",
                      "phone",
                      "whatsapp"
                    ]
                  },
                  "smsConsent": {
                    "type": "boolean",
                    "description": "API 2.0: SMS/WhatsApp checkbox ticked (only counts when the page offers it, see smsOptIn in GET /page)"
                  },
                  "returnUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where Stripe sends the visitor after the payment. Needs an allowed-websites list on the key with this origin on it (else 400 return_urls_need_allowed_origins / invalid_return_url); https (http only for localhost/127.0.0.1 when listed). Appended: taddoo_status=success&taddoo_session=<Stripe session id> (shop: taddoo_status=success&taddoo_order=<order token>)."
                  },
                  "cancelUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where the visitor lands after canceling in Stripe (through a signed taddoo.com link valid for 24 hours that releases held slots or stock). Same rules as returnUrl. Appended: taddoo_status=canceled (shop: plus taddoo_order). Default: returnUrl."
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "description": "Same fields as the JSON body plus up to 3 photos in 'images'",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Booked or payment required",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BookingResult"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bookings/{token}/submit": {
      "post": {
        "summary": "API 2.0: alias of POST /bookings/{token}",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,64}$"
            },
            "description": "bookingToken from GET /consultation, /touchup or /services"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "startAt",
                  "sessionId",
                  "name",
                  "email"
                ],
                "properties": {
                  "startAt": {
                    "type": "string"
                  },
                  "sessionId": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "phone": {
                    "type": "string",
                    "description": "international format, e.g. +41791234567"
                  },
                  "instagram": {
                    "type": "string"
                  },
                  "note": {
                    "type": "string"
                  },
                  "mode": {
                    "type": "string"
                  },
                  "smsConsent": {
                    "type": "boolean",
                    "description": "API 2.0: SMS/WhatsApp checkbox ticked (only counts when the page offers it, see smsOptIn in GET /page)"
                  },
                  "returnUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where Stripe sends the visitor after the payment. Needs an allowed-websites list on the key with this origin on it (else 400 return_urls_need_allowed_origins / invalid_return_url); https (http only for localhost/127.0.0.1 when listed). Appended: taddoo_status=success&taddoo_session=<Stripe session id> (shop: taddoo_status=success&taddoo_order=<order token>)."
                  },
                  "cancelUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where the visitor lands after canceling in Stripe (through a signed taddoo.com link valid for 24 hours that releases held slots or stock). Same rules as returnUrl. Appended: taddoo_status=canceled (shop: plus taddoo_order). Default: returnUrl."
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "description": "Same fields as the JSON body plus up to 3 photos in 'images'",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Booked or payment required",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BookingResult"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bookings/{token}/slots": {
      "get": {
        "summary": "API 2.0: free times for a booking option",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,64}$"
            },
            "description": "bookingToken from GET /consultation, /touchup or /services"
          },
          {
            "name": "sessionId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,64}$"
            },
            "description": "Random id per visitor, 8 to 64 chars (letters, digits, - and _)"
          },
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "in_person",
                "online",
                "phone",
                "whatsapp"
              ]
            },
            "description": "Consultation format (default: the first offered)"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "location",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "'home' or a location id"
          }
        ],
        "responses": {
          "200": {
            "description": "Free times",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BookingSlots"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bookings/{token}/hold": {
      "post": {
        "summary": "API 2.0: hold a time for 15 minutes",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,64}$"
            },
            "description": "bookingToken from GET /consultation, /touchup or /services"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "startAt",
                  "endAt",
                  "sessionId"
                ],
                "properties": {
                  "startAt": {
                    "type": "string"
                  },
                  "endAt": {
                    "type": "string"
                  },
                  "sessionId": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ expiresAt }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/bookings/{token}/release": {
      "post": {
        "summary": "API 2.0: release an own hold (without startAt all holds of this session)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{16,64}$"
            },
            "description": "bookingToken from GET /consultation, /touchup or /services"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "sessionId"
                ],
                "properties": {
                  "sessionId": {
                    "type": "string"
                  },
                  "startAt": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok: true }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/vouchers": {
      "get": {
        "summary": "Gift voucher amounts, customAmount { min, max }; 2.0: title, layout, imageUrl, previewImageUrl, theme, surcharge",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Gift voucher amounts, customAmount { min, max }; 2.0: title, layout, imageUrl, previewImageUrl, theme, surcharge",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/vouchers/checkout": {
      "post": {
        "summary": "Buy a gift voucher (returns a Stripe checkoutUrl)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "name",
                  "email"
                ],
                "properties": {
                  "amount": {
                    "type": "number"
                  },
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "recipient": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "returnUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where Stripe sends the visitor after the payment. Needs an allowed-websites list on the key with this origin on it (else 400 return_urls_need_allowed_origins / invalid_return_url); https (http only for localhost/127.0.0.1 when listed). Appended: taddoo_status=success&taddoo_session=<Stripe session id> (shop: taddoo_status=success&taddoo_order=<order token>)."
                  },
                  "cancelUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where the visitor lands after canceling in Stripe (through a signed taddoo.com link valid for 24 hours that releases held slots or stock). Same rules as returnUrl. Appended: taddoo_status=canceled (shop: plus taddoo_order). Default: returnUrl."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ checkoutUrl }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/vouchers/{token}": {
      "get": {
        "summary": "API 2.0: a bought voucher by its secret token (status, code, balance, pdfUrl)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Voucher",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/VoucherStatus"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/vouchers/by-session/{sessionId}": {
      "get": {
        "summary": "API 2.0: a bought voucher by the Stripe checkout session id (taddoo_session on the returnUrl); only within 3 hours after the purchase, 404 afterwards",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Voucher",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/VoucherStatus"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/shop": {
      "get": {
        "summary": "Merch shop: products with variants, stock and images, categories, shipping profiles (Etsy-style rates per country) and the customer fee; 2.0: title, theme",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Merch shop: products with variants, stock and images, categories, shipping profiles (Etsy-style rates per country) and the customer fee; 2.0: title, theme",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/shop/checkout": {
      "post": {
        "summary": "Pay a cart: returns a Stripe checkoutUrl plus the order number and token; reserves the stock for 30 minutes",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "items",
                  "country"
                ],
                "properties": {
                  "items": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 30,
                    "items": {
                      "type": "object",
                      "required": [
                        "variantId",
                        "quantity"
                      ],
                      "properties": {
                        "variantId": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "quantity": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 20
                        }
                      }
                    }
                  },
                  "country": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO code of the delivery country"
                  },
                  "returnUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where Stripe sends the visitor after the payment. Needs an allowed-websites list on the key with this origin on it (else 400 return_urls_need_allowed_origins / invalid_return_url); https (http only for localhost/127.0.0.1 when listed). Appended: taddoo_status=success&taddoo_session=<Stripe session id> (shop: taddoo_status=success&taddoo_order=<order token>)."
                  },
                  "cancelUrl": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "API 2.0: where the visitor lands after canceling in Stripe (through a signed taddoo.com link valid for 24 hours that releases held slots or stock). Same rules as returnUrl. Appended: taddoo_status=canceled (shop: plus taddoo_order). Default: returnUrl."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ checkoutUrl, order: { number, token } }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/shop/orders/{token}": {
      "get": {
        "summary": "Status of a shop order (pending, paid, shipped, canceled, refunded)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          },
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{32}$"
            },
            "description": "order.token from POST /shop/checkout (also taddoo_order on the returnUrl)"
          }
        ],
        "responses": {
          "200": {
            "description": "{ status, number, email }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/shop/release": {
      "post": {
        "summary": "API 2.0: the customer returned without paying; ends the checkout and puts the reserved stock back",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{32}$",
                    "description": "order token"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ released, status }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/stats": {
      "post": {
        "summary": "API 2.0: count a page view or a block click (shown in the artist's app)",
        "parameters": [
          {
            "$ref": "#/components/parameters/artist"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kind"
                ],
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "view",
                      "click"
                    ]
                  },
                  "blockId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "required for click: block id from GET /page"
                  },
                  "visitorId": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "server-side calls: anonymous id per visitor"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ ok: true }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/studio": {
      "get": {
        "summary": "Studio page with artists, opening hours, flash, full theme, todayWeekday and layout (studio key)",
        "parameters": [
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Studio",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/studio/form": {
      "get": {
        "summary": "Studio request form (studio key)",
        "parameters": [
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "responses": {
          "200": {
            "description": "Studio form",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/studio/form/submissions": {
      "post": {
        "summary": "Send a request to the studio (studio key)",
        "parameters": [
          {
            "$ref": "#/components/parameters/locale"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "answers"
                ],
                "properties": {
                  "artist": {
                    "type": "string",
                    "description": "artist id or 'any'"
                  },
                  "answers": {
                    "type": "object",
                    "description": "Keys: question ids (q_ prefix optional). Values: string, number, boolean (yes/no) or array.",
                    "additionalProperties": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "number"
                        },
                        {
                          "type": "boolean"
                        },
                        {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      ]
                    }
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "description": "q_<questionId> fields (repeat for several values) and image files in 'images'",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "{ status: received }",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Error"
          },
          "429": {
            "$ref": "#/components/responses/Error"
          },
          "500": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  }
}