{
  "openapi": "3.1.0",
  "info": {
    "title": "AdScaled API",
    "version": "1.0.0",
    "summary": "Public API of the AdScaled digital agency (www.adscaled.media): send a project inquiry to the team.",
    "description": "AdScaled is a Czech digital agency (performance marketing, web and app development, branding, photo and video).\nThis API backs the contact form on www.adscaled.media. Use `sendContactInquiry` only when a real person has asked\nyou to contact AdScaled on their behalf and has given you their name, e-mail and message. It e-mails the team and\nsends a confirmation to the given address. Do not use it for testing, marketing or bulk messages.\n\nNo authentication is required. Requests from browsers are limited to the AdScaled origins (CORS); server-side\nclients without an `Origin` header are accepted. Every rate-limited response carries the IETF\n`RateLimit-Policy` and `RateLimit` headers (draft-ietf-httpapi-ratelimit-headers-11); a `429` also carries\n`Retry-After` in seconds. All `/api` paths share a limit of 300 requests per minute per client IP; the contact\nendpoint allows 5 messages per 15 minutes per client IP.",
    "contact": {
      "name": "AdScaled",
      "email": "contact@adscaled.media",
      "url": "https://www.adscaled.media/en/contact"
    }
  },
  "externalDocs": {
    "description": "Human-readable API documentation",
    "url": "https://api.adscaled.media/api/docs"
  },
  "servers": [
    {
      "url": "https://api.adscaled.media",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Status",
      "description": "Service availability."
    },
    {
      "name": "Contact",
      "description": "Project inquiries sent to the AdScaled team by e-mail."
    }
  ],
  "paths": {
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "tags": [
          "Status"
        ],
        "summary": "Check API availability",
        "description": "Returns `ok: true` when the API is running and whether e-mail delivery for the contact endpoint is configured.",
        "responses": {
          "200": {
            "description": "The API is running.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                },
                "example": {
                  "ok": true,
                  "mailConfigured": true
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/api/contact": {
      "post": {
        "operationId": "sendContactInquiry",
        "tags": [
          "Contact"
        ],
        "summary": "Send a project inquiry to AdScaled",
        "description": "E-mails the inquiry to the AdScaled team (Reply-To is the sender) and sends the sender a confirmation e-mail in the chosen language. Only call this on behalf of a real person who asked to contact AdScaled. The team usually answers within one business day. Text fields are trimmed and truncated to the stated maximum length.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactInquiry"
              },
              "example": {
                "name": "Jana Nováková",
                "email": "jana@example.com",
                "phone": "+420 777 123 456",
                "subject": "Website",
                "message": "We need a new website for our e-shop and Meta Ads campaigns. Budget around 150k CZK.",
                "language": "en"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The inquiry was delivered to the team.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                },
                "example": {
                  "message": "Your message has arrived. We will get back to you as soon as possible."
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (name shorter than 2 characters, invalid e-mail or phone, message shorter than 10 characters) or the body is not valid JSON.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "403": {
            "description": "The request came from a browser origin that is not allowed.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "413": {
            "description": "The JSON body is larger than 32 kB.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "description": "The e-mail could not be sent. Ask the person to write to contact@adscaled.media directly.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "503": {
            "description": "E-mail delivery is temporarily not configured. Ask the person to write to contact@adscaled.media directly.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Health": {
        "type": "object",
        "required": [
          "ok",
          "mailConfigured"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true,
            "description": "Always true when the API answers."
          },
          "mailConfigured": {
            "type": "boolean",
            "description": "Whether `sendContactInquiry` can currently deliver e-mail."
          }
        }
      },
      "ContactInquiry": {
        "type": "object",
        "required": [
          "name",
          "email",
          "message"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 2,
            "maxLength": 100,
            "description": "Full name of the person or company contact."
          },
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 160,
            "description": "Reply address; the confirmation e-mail is sent here."
          },
          "phone": {
            "type": "string",
            "maxLength": 40,
            "pattern": "^\\+?[\\d\\s()./-]+$",
            "description": "Optional phone number with 9–15 digits."
          },
          "subject": {
            "type": "string",
            "maxLength": 100,
            "description": "Optional topic, e.g. \"Website\", \"Performance marketing\", \"Branding\"."
          },
          "message": {
            "type": "string",
            "minLength": 10,
            "maxLength": 5000,
            "description": "What the person needs: project, goals, timeline, budget."
          },
          "language": {
            "type": "string",
            "enum": [
              "cs",
              "en",
              "ru",
              "de",
              "fr"
            ],
            "default": "cs",
            "description": "Language of the response messages and of the confirmation e-mail."
          },
          "website": {
            "type": "string",
            "maxLength": 0,
            "description": "Spam trap. Must be omitted or empty."
          }
        },
        "additionalProperties": false
      },
      "Message": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable, localized result message."
          }
        }
      }
    },
    "headers": {
      "RateLimit": {
        "description": "Remaining quota per policy (draft-ietf-httpapi-ratelimit-headers-11), e.g. `\"flood\";r=299;t=60, \"contact\";r=4;t=900`.",
        "schema": {
          "type": "string"
        }
      },
      "RateLimitPolicy": {
        "description": "Quota policies that apply to this request, e.g. `\"flood\";q=300;w=60, \"contact\";q=5;w=900`.",
        "schema": {
          "type": "string"
        }
      },
      "RetryAfter": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 1
        }
      }
    },
    "responses": {
      "TooManyRequests": {
        "description": "Rate limit exceeded. Wait for `Retry-After` seconds before the next request.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "Retry-After": {
            "$ref": "#/components/headers/RetryAfter"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Message"
            }
          }
        }
      }
    }
  }
}