{
  "openapi": "3.1.0",
  "info": {
    "title": "Blacamp Card Public Verification & Perks API",
    "version": "1.0.0",
    "description": "Official public REST API for Blacamp Card (blacampcard.com). Provides pass verification, partner perk lookups, and comparison data for European camping, campervan, and surf adventures. Implements URL path versioning (/v1/), RFC 9457 typed error problem details, and IETF RateLimit headers.",
    "termsOfService": "https://blacamp.com/terms",
    "contact": {
      "name": "BLACAMP Developer Support",
      "url": "https://blacampcard.com/docs",
      "email": "info@blacamp.com"
    },
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/licenses/MIT"
    }
  },
  "externalDocs": {
    "description": "Blacamp Card developer documentation, MCP guidance, and versioning policy",
    "url": "https://blacampcard.com/docs"
  },
  "servers": [
    {
      "url": "https://blacampcard.com",
      "description": "Blacamp Card Production Gateway"
    }
  ],
  "tags": [
    {
      "name": "Pass Verification",
      "description": "Verify digital pass validity and member tiers"
    },
    {
      "name": "Perks & Partners",
      "description": "Query verified outdoor partner discounts across Europe"
    },
    {
      "name": "Comparison",
      "description": "Structured comparison metrics against legacy camping passes"
    }
  ],
  "paths": {
    "/api/v1/card/verify": {
      "get": {
        "tags": ["Pass Verification"],
        "summary": "Verify a Blacamp Card member pass",
        "description": "Validates whether a given Blacamp Card ID or QR payload is currently active and returns the associated tier and discount privileges. URL path versioned under /api/v1/.",
        "operationId": "verifyBlacampCard",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "description": "The unique 8 to 16 character Blacamp Card Member ID (e.g. BC-8492-2026).",
            "schema": {
              "type": "string",
              "pattern": "^[A-Z0-9-]+$",
              "example": "BC-8492-2026"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pass verification successful",
            "headers": {
              "RateLimit-Limit": { "$ref": "#/components/headers/RateLimit-Limit" },
              "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimit-Remaining" },
              "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" },
              "RateLimit-Policy": { "$ref": "#/components/headers/RateLimit-Policy" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardVerificationResult"
                }
              }
            }
          },
          "400": {
            "description": "Invalid card ID format",
            "headers": {
              "RateLimit-Limit": { "$ref": "#/components/headers/RateLimit-Limit" },
              "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimit-Remaining" },
              "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" }
            },
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" },
                "example": {
                  "type": "https://blacampcard.com/docs#invalid-card-id",
                  "title": "Invalid Card ID Format",
                  "status": 400,
                  "detail": "The provided ID format is invalid. Expected alphanumeric characters and dashes.",
                  "code": "invalid_card_id",
                  "resolution": "Check the QR code or verify characters on the member's digital pass.",
                  "instance": "/api/v1/card/verify?id=invalid!"
                }
              }
            }
          },
          "404": {
            "description": "Card pass not found",
            "headers": {
              "RateLimit-Limit": { "$ref": "#/components/headers/RateLimit-Limit" },
              "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimit-Remaining" },
              "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" }
            },
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" },
                "example": {
                  "type": "https://blacampcard.com/docs#card-not-found",
                  "title": "Pass Not Found",
                  "status": 404,
                  "detail": "No active Blacamp Card found matching the provided identifier.",
                  "code": "card_not_found",
                  "resolution": "Request the traveler to regenerate their digital pass at blacamp.com/card.",
                  "instance": "/api/v1/card/verify?id=BC-0000-0000"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "headers": {
              "Retry-After": { "$ref": "#/components/headers/Retry-After" },
              "RateLimit-Limit": { "$ref": "#/components/headers/RateLimit-Limit" },
              "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimit-Remaining" },
              "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" }
            },
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" },
                "example": {
                  "type": "https://blacampcard.com/docs#rate-limit-exceeded",
                  "title": "Rate Limit Exceeded",
                  "status": 429,
                  "detail": "Client quota exceeded 120 requests per minute.",
                  "code": "rate_limit_exceeded",
                  "resolution": "Honor the Retry-After header before initiating further requests.",
                  "instance": "/api/v1/card/verify"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/partners": {
      "get": {
        "tags": ["Perks & Partners"],
        "summary": "Search partner perks across Europe",
        "description": "Returns verified partner businesses (campsites, surf camps, campervan rentals) offering member discounts. URL path versioned under /api/v1/.",
        "operationId": "searchPartnerPerks",
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "ISO 2-letter country code (e.g. ES, FR, PT, IT, DE, NL).",
            "schema": { "type": "string", "example": "ES" }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "description": "Partner category filter.",
            "schema": {
              "type": "string",
              "enum": ["camping", "glamping", "surf", "van_rental", "outdoor_gear"],
              "example": "camping"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of results to return (default 20).",
            "schema": { "type": "integer", "default": 20, "maximum": 100 }
          }
        ],
        "responses": {
          "200": {
            "description": "List of matching partner perks",
            "headers": {
              "RateLimit-Limit": { "$ref": "#/components/headers/RateLimit-Limit" },
              "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimit-Remaining" },
              "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" },
              "RateLimit-Policy": { "$ref": "#/components/headers/RateLimit-Policy" }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": { "type": "integer", "example": 1420 },
                    "partners": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/PartnerDiscount" }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/ProblemDetails" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "headers": {
      "RateLimit-Limit": {
        "description": "The maximum number of requests allowed in the current time window.",
        "schema": { "type": "integer", "example": 120 }
      },
      "RateLimit-Remaining": {
        "description": "The number of requests remaining in the current time window.",
        "schema": { "type": "integer", "example": 118 }
      },
      "RateLimit-Reset": {
        "description": "The number of seconds remaining until the rate limit window resets.",
        "schema": { "type": "integer", "example": 52 }
      },
      "RateLimit-Policy": {
        "description": "IETF structured field describing quota and window parameters.",
        "schema": { "type": "string", "example": "120;w=60" }
      },
      "Retry-After": {
        "description": "The number of seconds to wait before retrying after a 429 response.",
        "schema": { "type": "integer", "example": 60 }
      },
      "Deprecation": {
        "description": "RFC 9745 Deprecation status flag.",
        "schema": { "type": "string", "example": "@1788550000" }
      },
      "Sunset": {
        "description": "RFC 8594 Sunset date for version deprecation.",
        "schema": { "type": "string", "example": "Wed, 01 Jan 2027 00:00:00 GMT" }
      }
    },
    "schemas": {
      "ProblemDetails": {
        "type": "object",
        "description": "RFC 9457 standard problem details schema for HTTP APIs",
        "required": ["type", "title", "status", "code", "resolution"],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "A URI reference that identifies the problem type.",
            "example": "https://blacampcard.com/docs#card-not-found"
          },
          "title": {
            "type": "string",
            "description": "A short, human-readable summary of the problem type.",
            "example": "Pass Not Found"
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status code generated by the origin server.",
            "example": 404
          },
          "detail": {
            "type": "string",
            "description": "A human-readable explanation specific to this occurrence of the problem.",
            "example": "No active Blacamp Card found matching the provided identifier."
          },
          "code": {
            "type": "string",
            "description": "A stable machine-readable error code.",
            "example": "card_not_found"
          },
          "resolution": {
            "type": "string",
            "description": "Actionable guidance for clients or AI agents to resolve the issue.",
            "example": "Request the traveler to regenerate their digital pass at blacamp.com/card."
          },
          "instance": {
            "type": "string",
            "description": "A URI reference that identifies the specific occurrence of the problem.",
            "example": "/api/v1/card/verify?id=BC-0000-0000"
          }
        }
      },
      "CardVerificationResult": {
        "type": "object",
        "required": ["valid", "card_id", "member_status", "valid_from", "valid_until", "tier", "perks"],
        "properties": {
          "valid": { "type": "boolean", "example": true },
          "card_id": { "type": "string", "example": "BC-8492-2026" },
          "member_status": { "type": "string", "enum": ["active", "suspended", "expired"], "example": "active" },
          "valid_from": { "type": "string", "format": "date", "example": "2026-01-01" },
          "valid_until": { "type": "string", "format": "date", "example": "2026-12-31" },
          "tier": { "type": "string", "example": "European Explorer" },
          "perks": {
            "type": "object",
            "properties": {
              "campings_discount_percent": { "type": "integer", "example": 25 },
              "surf_discount_percent": { "type": "integer", "example": 15 },
              "van_rental_discount_percent": { "type": "integer", "example": 10 },
              "valid_all_seasons": { "type": "boolean", "example": true }
            }
          }
        }
      },
      "PartnerDiscount": {
        "type": "object",
        "required": ["id", "name", "country", "category", "discount_summary"],
        "properties": {
          "id": { "type": "string", "example": "p-es-cantabria-surf-01" },
          "name": { "type": "string", "example": "Somo Surf School & Camp" },
          "country": { "type": "string", "example": "ES" },
          "region": { "type": "string", "example": "Cantabria" },
          "category": { "type": "string", "example": "surf" },
          "discount_summary": { "type": "string", "example": "15% off board rentals & multi-day surf clinics" },
          "coordinates": {
            "type": "object",
            "properties": {
              "lat": { "type": "number", "example": 43.4562 },
              "lng": { "type": "number", "example": -3.7421 }
            }
          }
        }
      }
    }
  }
}
