{
  "openapi": "3.1.0",
  "info": {
    "title": "VayaPin API",
    "version": "1.0.0",
    "description": "Welcome to our API documentation! This is the VayaPin API, a public REST API for accessing VayaPin data. This API is designed to be used by developers and businesses to access VayaPin data.",
    "contact": {
      "name": "VayaPin Support",
      "email": "support@vayapin.com"
    }
  },
  "paths": {
    "/v1/pins/{id}": {
      "get": {
        "summary": "/v1/pins/{id}",
        "tags": [
          "Pins"
        ],
        "description": "Retrieve pin data by its VayaPin identifier. Returns data based on account abilities and excludes password-protected fields.\n",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "VayaPin identifier (e.g., DK:EXAMPLE)",
            "example": "DK:EXAMPLE",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "successful",
            "content": {
              "application/json": {
                "examples": {
                  "successful_example": {
                    "value": {
                      "data": {
                        "country": "DK",
                        "name": "EXAMPLE",
                        "vayapin": "DK:EXAMPLE",
                        "qrcode": {
                          "id": "123e4567-e89b-12d3-a456-426614174000",
                          "handle": "webapp",
                          "value": "https://vaya.to/DK:EXAMPLE?o=wa",
                          "img_url": "https://api.vayapin.com/r/qrc/p/DK:EXAMPLE/webapp/dk_example.png"
                        },
                        "location": {
                          "encrypted": false,
                          "value": {
                            "lat": 55.6761,
                            "lng": 12.5683
                          }
                        },
                        "title": {
                          "encrypted": false,
                          "value": "Example Business"
                        },
                        "subtitle": {
                          "encrypted": false,
                          "value": "Your trusted partner"
                        },
                        "description": {
                          "encrypted": false,
                          "value": "We provide excellent services to our customers"
                        },
                        "phone": {
                          "encrypted": false,
                          "value": "+45 12345678"
                        },
                        "email": {
                          "encrypted": false,
                          "value": "contact@example.dk"
                        },
                        "website": {
                          "encrypted": false,
                          "value": "https://example.dk"
                        },
                        "whatsapp_phone": null,
                        "address_first_name": null,
                        "address_last_name": null,
                        "address_contact_name": {
                          "encrypted": false,
                          "value": "John Doe"
                        },
                        "address_road": {
                          "encrypted": false,
                          "value": "Main Street"
                        },
                        "address_house": null,
                        "address_house_number": {
                          "encrypted": false,
                          "value": "123"
                        },
                        "address_addition": null,
                        "address_settlement": {
                          "encrypted": false,
                          "value": "Copenhagen"
                        },
                        "address_settlement_part": null,
                        "address_postcode": {
                          "encrypted": false,
                          "value": "1000"
                        },
                        "address_region": {
                          "encrypted": false,
                          "value": "Capital Region"
                        },
                        "address_region_part": null,
                        "business_address": {
                          "business_name": "Example Business Ltd.",
                          "first_name": "John",
                          "last_name": "Doe",
                          "contact_name": "John Doe",
                          "road": "Main Street",
                          "house": null,
                          "house_number": "123",
                          "addition": null,
                          "settlement": "Copenhagen",
                          "settlement_part": null,
                          "postcode": "1000",
                          "region": "Capital Region",
                          "region_part": null
                        }
                      }
                    },
                    "summary": "A complete pin response with all available fields"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "country": {
                          "type": "string",
                          "description": "Country code in uppercase"
                        },
                        "name": {
                          "type": "string",
                          "description": "Pin name in uppercase"
                        },
                        "vayapin": {
                          "type": "string",
                          "description": "VayaPin identifier in format COUNTRY:NAME"
                        },
                        "qrcode": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "properties": {
                            "id": {
                              "type": "string",
                              "description": "QR code UUID"
                            },
                            "handle": {
                              "type": "string",
                              "description": "QR code type handle (e.g., webapp)"
                            },
                            "value": {
                              "type": "string",
                              "description": "URL encoded in the QR code"
                            },
                            "img_url": {
                              "type": "string",
                              "description": "URL to PNG image of the QR code"
                            }
                          },
                          "required": [
                            "id",
                            "handle",
                            "value",
                            "img_url"
                          ]
                        },
                        "location": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "properties": {
                            "encrypted": {
                              "type": "boolean",
                              "description": "Whether the field is encrypted"
                            },
                            "value": {
                              "type": [
                                "object",
                                "null"
                              ],
                              "properties": {
                                "lat": {
                                  "type": "number",
                                  "format": "float",
                                  "description": "Latitude"
                                },
                                "lng": {
                                  "type": "number",
                                  "format": "float",
                                  "description": "Longitude"
                                }
                              }
                            }
                          }
                        },
                        "title": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "subtitle": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "description": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "phone": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "email": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "website": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "whatsapp_phone": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_first_name": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_last_name": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_contact_name": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_road": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_house": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_house_number": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_addition": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_settlement": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_settlement_part": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_postcode": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_region": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "address_region_part": {
                          "$ref": "#/components/schemas/PinDataField"
                        },
                        "business_address": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "description": "Business address information",
                          "properties": {
                            "business_name": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Business/company name"
                            },
                            "first_name": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Contact first name"
                            },
                            "last_name": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Contact last name"
                            },
                            "contact_name": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Full contact name"
                            },
                            "road": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Street/road name"
                            },
                            "house": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Building name"
                            },
                            "house_number": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Street number"
                            },
                            "addition": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Additional address info"
                            },
                            "settlement": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "City/town"
                            },
                            "settlement_part": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "City district/neighborhood"
                            },
                            "postcode": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "ZIP/postal code"
                            },
                            "region": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "State/province"
                            },
                            "region_part": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "County/region subdivision"
                            }
                          }
                        }
                      },
                      "required": [
                        "country",
                        "name",
                        "vayapin"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "forbidden - expired subscription",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaExceededErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/pins": {
      "post": {
        "summary": "/v1/pins",
        "tags": [
          "Pins"
        ],
        "description": "Create a new pin by provisioning an account, subscription request, and data set update.\n\nThe full VayaPin identifier of the new pin is `data.account.default_country` + `:` + `data.pin_name`\n(for example `DE:EXAMPLE`).\n\nA `207 Multi-Status` response means the pin was created but some sections of its data set could\nnot be saved; `data.pin_data_set_update.sections` reports the result of every section.\n\nRequired address fields depend on `country`. Most countries need `address_line_1`, `zip_code`\nand `city`; some need fewer or also `state`, and some check the `zip_code` format. A missing or\nmalformed field is reported in `data.errors` of the `422` response, for example\n`\"city\": [\"is required for UA addresses\"]`.\n",
        "parameters": [],
        "responses": {
          "200": {
            "description": "pin created",
            "content": {
              "application/json": {
                "examples": {
                  "successful_example": {
                    "value": {
                      "data": {
                        "valid": true,
                        "pin_name": "EXAMPLE",
                        "requested_name": "EXAMPLE",
                        "alternative_names": [],
                        "errors": [],
                        "account": {
                          "id": "3f6c1c46-9a38-4fa0-89d5-0d1973c9c827",
                          "name": "New Account",
                          "default_country": "DE"
                        },
                        "subscription_request": {
                          "id": "c6f8b1d9-7b0e-4d1f-8b2a-7f2b9e88d2b3",
                          "state": "done",
                          "subscription_id": "9d0a3c52-1f7e-4c2b-a1a4-2f3e5d6c7b8a",
                          "pin_data_set_id": "5b1e2d3c-4a5f-4e6d-9c8b-7a6f5e4d3c2b",
                          "failed_state": null,
                          "system_error_message": null,
                          "error_message": null
                        },
                        "pin_data_set_update": {
                          "sections": {
                            "base_data": {
                              "success": true
                            }
                          },
                          "data": {
                            "email": {
                              "value": "john@example.com",
                              "encrypted": false
                            }
                          }
                        }
                      }
                    },
                    "summary": "Pin created successfully"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "valid": {
                          "type": "boolean"
                        },
                        "pin_name": {
                          "type": "string",
                          "description": "Pin name without the country prefix",
                          "example": "EXAMPLE"
                        },
                        "requested_name": {
                          "type": "string"
                        },
                        "alternative_names": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "errors": {
                          "type": [
                            "array",
                            "null"
                          ],
                          "items": {
                            "type": "string"
                          }
                        },
                        "account": {
                          "type": "object",
                          "description": "Account the pin was created for",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "default_country": {
                              "type": "string",
                              "description": "ISO 3166-1 alpha-2 country code; the prefix of the VayaPin identifier",
                              "example": "DE"
                            },
                            "created_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date-time"
                            },
                            "updated_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date-time"
                            },
                            "creation_source": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "example": "api:v1:token-42"
                            }
                          },
                          "additionalProperties": true
                        },
                        "subscription_request": {
                          "type": "object",
                          "description": "Pin subscription request that provisioned the pin",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "state": {
                              "type": "string",
                              "example": "done"
                            },
                            "subscription_id": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "uuid"
                            },
                            "pin_data_set_id": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "uuid"
                            },
                            "failed_state": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "system_error_message": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "error_message": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          }
                        },
                        "pin_data_set_update": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "additionalProperties": true,
                          "description": "Per-section result (`sections`) and the saved data set (`data`)"
                        }
                      },
                      "required": [
                        "valid",
                        "pin_name",
                        "requested_name"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "207": {
            "description": "pin created, data set partially saved",
            "content": {
              "application/json": {
                "examples": {
                  "partial_example": {
                    "value": {
                      "data": {
                        "valid": true,
                        "pin_name": "EXAMPLE",
                        "requested_name": "EXAMPLE",
                        "alternative_names": [],
                        "errors": [],
                        "account": {
                          "id": "3f6c1c46-9a38-4fa0-89d5-0d1973c9c827",
                          "name": "New Account",
                          "default_country": "DE"
                        },
                        "subscription_request": {
                          "id": "c6f8b1d9-7b0e-4d1f-8b2a-7f2b9e88d2b3",
                          "state": "done",
                          "subscription_id": "9d0a3c52-1f7e-4c2b-a1a4-2f3e5d6c7b8a",
                          "pin_data_set_id": "5b1e2d3c-4a5f-4e6d-9c8b-7a6f5e4d3c2b",
                          "failed_state": null,
                          "system_error_message": null,
                          "error_message": null
                        },
                        "pin_data_set_update": {
                          "sections": {
                            "base_data": {
                              "success": true,
                              "errors": []
                            },
                            "pin_data_location": {
                              "success": false,
                              "errors": [
                                "Latitude is invalid"
                              ]
                            }
                          },
                          "data": {
                            "email": {
                              "value": "john@example.com",
                              "encrypted": false
                            }
                          }
                        }
                      }
                    },
                    "summary": "Pin created, but the location section failed to save"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "valid": {
                          "type": "boolean"
                        },
                        "pin_name": {
                          "type": "string",
                          "description": "Pin name without the country prefix",
                          "example": "EXAMPLE"
                        },
                        "requested_name": {
                          "type": "string"
                        },
                        "alternative_names": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "errors": {
                          "type": [
                            "array",
                            "null"
                          ],
                          "items": {
                            "type": "string"
                          }
                        },
                        "account": {
                          "type": "object",
                          "description": "Account the pin was created for",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "name": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "default_country": {
                              "type": "string",
                              "description": "ISO 3166-1 alpha-2 country code; the prefix of the VayaPin identifier",
                              "example": "DE"
                            },
                            "created_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date-time"
                            },
                            "updated_at": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "date-time"
                            },
                            "creation_source": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "example": "api:v1:token-42"
                            }
                          },
                          "additionalProperties": true
                        },
                        "subscription_request": {
                          "type": "object",
                          "description": "Pin subscription request that provisioned the pin",
                          "properties": {
                            "id": {
                              "type": "string",
                              "format": "uuid"
                            },
                            "state": {
                              "type": "string",
                              "example": "done"
                            },
                            "subscription_id": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "uuid"
                            },
                            "pin_data_set_id": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "format": "uuid"
                            },
                            "failed_state": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "system_error_message": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "error_message": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          }
                        },
                        "pin_data_set_update": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "additionalProperties": true,
                          "description": "Per-section result (`sections`) and the saved data set (`data`)"
                        }
                      },
                      "required": [
                        "valid",
                        "pin_name",
                        "requested_name"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "too many requests",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaExceededErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "validation error - invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ]
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "errors": {
                          "type": "object",
                          "additionalProperties": true
                        }
                      },
                      "required": [
                        "errors"
                      ]
                    }
                  },
                  "required": [
                    "error",
                    "data"
                  ]
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "data": {
                    "type": "object",
                    "properties": {
                      "country": {
                        "type": "string",
                        "description": "ISO 3166-1 alpha-2 country code",
                        "example": "DE"
                      },
                      "name": {
                        "type": "string",
                        "description": "Full name (first + last), used to derive pin name",
                        "example": "John Example"
                      },
                      "first_name": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "First name (overrides split from name)",
                        "example": "John"
                      },
                      "last_name": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Last name (overrides split from name)",
                        "example": "Example"
                      },
                      "email": {
                        "type": "string",
                        "format": "email",
                        "example": "john@example.com"
                      },
                      "phone_number": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "+1234567890"
                      },
                      "address_line_1": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Street and number",
                        "example": "Example Street 1"
                      },
                      "address_line_2": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "Suite 2"
                      },
                      "zip_code": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "10115"
                      },
                      "city": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "Berlin"
                      },
                      "state": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "BE"
                      }
                    },
                    "required": [
                      "country",
                      "name",
                      "email"
                    ]
                  }
                },
                "required": [
                  "data"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/requests": {
      "post": {
        "summary": "/v1/requests",
        "tags": [
          "Requests"
        ],
        "description": "Ask VayaPin for a recipient's address on behalf of a merchant (VayaConnect store plugins).\n\nThe account must have an enabled webhook endpoint provisioned before it can create requests;\nthe resolved address is always delivered to that endpoint, never in this response.\n\nTwo outcomes are possible, both returned as `201 Created` and distinguished by `data.status`:\n\n* `resolved` (express path) - the recipient's email or phone already has a pin. The pin is\n  linked to the request and the address webhook is dispatched immediately.\n* `pending` - no pin matched, so a claimable pin is created and the recipient is emailed a\n  claim link. The webhook fires later, once the recipient completes or updates their pin.\n\nSend `idempotency_key` (for example the merchant's order id) to make retries safe: a repeat\ncall with a key already used by the account returns the original request with `200 OK`\ninstead of creating a second one. The key is read from the request body only; an\n`Idempotency-Key` HTTP header is ignored.\n\nPhone-only requests are not supported yet - `email` is required.\n\nThe resolved address is delivered as a `pin.address_updated` webhook to the account's webhook\nendpoint (set up in the VayaPin dashboard under Developer > VayaCore > Webhooks) - see Webhooks.\n",
        "callbacks": {
          "pin.address_updated": {
            "https://yourstore.example.com/wp-json/vayapin/v1/webhooks": {
              "post": {
                "summary": "pin.address_updated webhook",
                "security": [],
                "description": "Sent to the webhook endpoint URL registered for the account in the VayaPin dashboard\n(Developer > VayaCore > Webhooks). Sent when a POST /v1/requests request resolves (immediately\non the express path, otherwise once the recipient completes their pin) and again whenever the\nlinked pin's address changes later.\n\nThe body is signed with HMAC-SHA256 over the exact raw request body, using the endpoint's\nsigning secret shown in the dashboard when the webhook is created. The signature is sent as\n`X-VayaPin-Signature: sha256=<hex>`; compare it in constant time before trusting the payload.\n\nRespond with any `2xx` to acknowledge. `5xx` responses and timeouts are retried (up to\n5 attempts) with the same `event_id`, so deduplicate on it; `4xx` responses are not retried.\n",
                "parameters": [
                  {
                    "name": "X-VayaPin-Signature",
                    "in": "header",
                    "required": true,
                    "description": "HMAC-SHA256 of the raw body keyed with the endpoint signing secret, formatted as `sha256=<hex>`",
                    "schema": {
                      "type": "string",
                      "example": "sha256=3f1c9b..."
                    }
                  },
                  {
                    "name": "X-VayaPin-Event-Id",
                    "in": "header",
                    "required": true,
                    "description": "Same value as `event_id` in the body; stable across retries",
                    "schema": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                ],
                "requestBody": {
                  "required": true,
                  "content": {
                    "application/json": {
                      "schema": {
                        "type": "object",
                        "properties": {
                          "event_id": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Unique event identifier; reused on retries of the same event"
                          },
                          "event_type": {
                            "type": "string",
                            "enum": [
                              "pin.address_updated"
                            ]
                          },
                          "occurred_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "request_id": {
                            "type": "string",
                            "format": "uuid",
                            "description": "`data.request_id` returned by POST /v1/requests"
                          },
                          "vayapin_id": {
                            "type": "string",
                            "example": "DK:LARSEN12"
                          },
                          "scripts": {
                            "type": "object",
                            "description": "Formatted one-line address per language. `latin` (English) is always present; `native` is added when the country's default language is not English and a native address could be resolved (e.g. Danish for DK).",
                            "properties": {
                              "latin": {
                                "$ref": "#/components/schemas/WebhookAddressScript"
                              },
                              "native": {
                                "$ref": "#/components/schemas/WebhookAddressScript"
                              }
                            },
                            "required": [
                              "latin"
                            ]
                          },
                          "gps": {
                            "type": [
                              "object",
                              "null"
                            ],
                            "properties": {
                              "lat": {
                                "type": "number",
                                "format": "float",
                                "example": 55.6761
                              },
                              "lng": {
                                "type": "number",
                                "format": "float",
                                "example": 12.5683
                              }
                            }
                          },
                          "qrcode": {
                            "type": "object",
                            "properties": {
                              "img_url": {
                                "type": "string",
                                "format": "uri"
                              }
                            }
                          },
                          "contact": {
                            "type": "object",
                            "description": "Recipient contact details stored on the pin, for reconciliation",
                            "properties": {
                              "email": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "phone": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "required": [
                          "event_id",
                          "event_type",
                          "occurred_at",
                          "request_id",
                          "vayapin_id",
                          "scripts",
                          "qrcode",
                          "contact"
                        ]
                      },
                      "example": {
                        "event_id": "0b6c2f4e-8d1a-4c3b-9e7f-1a2b3c4d5e6f",
                        "event_type": "pin.address_updated",
                        "occurred_at": "2026-10-08T12:34:56Z",
                        "request_id": "c6f8b1d9-7b0e-4d1f-8b2a-7f2b9e88d2b3",
                        "vayapin_id": "DK:LARSEN12",
                        "scripts": {
                          "latin": {
                            "label": "Vesterbrogade 1, 1620 København V, Danmark",
                            "display": "Vesterbrogade 1, 1620 København V, Danmark"
                          }
                        },
                        "gps": {
                          "lat": 55.6761,
                          "lng": 12.5683
                        },
                        "qrcode": {
                          "img_url": "https://api.vayapin.com/r/qrc/p/DK:LARSEN12/webapp/DK_LARSEN12.png"
                        },
                        "contact": {
                          "email": "lars@example.com",
                          "phone": "+4512345678",
                          "name": "Lars Larsen"
                        }
                      }
                    }
                  }
                },
                "responses": {
                  "200": {
                    "description": "Any 2xx acknowledges the event"
                  },
                  "401": {
                    "description": "Signature rejected; not retried"
                  },
                  "500": {
                    "description": "Receiver error; retried with the same event_id"
                  }
                }
              }
            }
          }
        },
        "parameters": [],
        "responses": {
          "201": {
            "description": "request created",
            "content": {
              "application/json": {
                "examples": {
                  "pending_example": {
                    "value": {
                      "data": {
                        "request_id": "c6f8b1d9-7b0e-4d1f-8b2a-7f2b9e88d2b3",
                        "status": "pending",
                        "vayapin_id": "DK:EXAMPLE"
                      }
                    },
                    "summary": "No pin matched - a claimable pin was created and the recipient emailed a claim link"
                  },
                  "resolved_example": {
                    "value": {
                      "data": {
                        "request_id": "3f6c1c46-9a38-4fa0-89d5-0d1973c9c827",
                        "status": "resolved",
                        "vayapin_id": "DK:EXAMPLE"
                      }
                    },
                    "summary": "Express path - an existing pin matched and the address webhook was dispatched"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string",
                          "format": "uuid",
                          "description": "VayaPin identifier of the address request"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "resolved"
                          ],
                          "description": "pending - recipient was emailed a claim link; resolved - an existing pin matched and the webhook was dispatched"
                        },
                        "vayapin_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "VayaPin identifier of the linked pin (e.g. DK:EXAMPLE)",
                          "example": "DK:EXAMPLE"
                        }
                      },
                      "required": [
                        "request_id",
                        "status",
                        "vayapin_id"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "200": {
            "description": "request replayed (idempotency key already used)",
            "content": {
              "application/json": {
                "examples": {
                  "replayed_example": {
                    "value": {
                      "data": {
                        "request_id": "c6f8b1d9-7b0e-4d1f-8b2a-7f2b9e88d2b3",
                        "status": "pending",
                        "vayapin_id": "DK:EXAMPLE"
                      }
                    },
                    "summary": "The original request is returned; no new request is created"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "resolved"
                          ]
                        },
                        "vayapin_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "example": "DK:EXAMPLE"
                        }
                      },
                      "required": [
                        "request_id",
                        "status",
                        "vayapin_id"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "request rejected",
            "content": {
              "application/json": {
                "examples": {
                  "endpoint_not_provisioned_example": {
                    "value": {
                      "error": {
                        "code": "endpoint_not_provisioned",
                        "message": "No enabled webhook endpoint for this account. Provision one before creating requests."
                      },
                      "data": {}
                    },
                    "summary": "The account has no enabled webhook endpoint"
                  },
                  "email_required_example": {
                    "value": {
                      "error": {
                        "code": "email_required",
                        "message": "email is required (phone-only requests are not supported yet)"
                      },
                      "data": {}
                    },
                    "summary": "The payload carries no email"
                  },
                  "invalid_input_example": {
                    "value": {
                      "error": {
                        "code": "invalid_input",
                        "message": "Invalid input"
                      },
                      "data": {
                        "errors": {
                          "country": [
                            "can't be blank",
                            "is the wrong length (should be 2 characters)"
                          ]
                        }
                      }
                    },
                    "summary": "A field failed validation; data.errors lists the messages per field"
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "object",
                      "properties": {
                        "code": {
                          "type": "string",
                          "description": "Machine-readable error code",
                          "example": "endpoint_not_provisioned"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable error message"
                        }
                      },
                      "required": [
                        "code",
                        "message"
                      ]
                    },
                    "data": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "Empty for request-level failures; carries pin pipeline details when the underlying pin could not be created"
                    }
                  },
                  "required": [
                    "error",
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "body is not wrapped in data",
            "content": {
              "application/json": {
                "examples": {
                  "missing_data_example": {
                    "value": {
                      "error": {
                        "code": "bad_request",
                        "message": "param is missing or the value is empty: data",
                        "request_id": "5862b2ced630f61e3c2fe0f9a5cd0c4a"
                      }
                    },
                    "summary": "Fields were sent at the top level instead of inside data"
                  }
                },
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuotaExceededErrorResponse"
                }
              }
            }
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "data": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "Full name of the recipient (first + last), used to derive the pin name",
                        "example": "John Example"
                      },
                      "first_name": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "First name (overrides the split from name)",
                        "example": "John"
                      },
                      "last_name": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Last name (overrides the split from name)",
                        "example": "Example"
                      },
                      "email": {
                        "type": "string",
                        "format": "email",
                        "description": "Recipient email. Required - it is both the pin match key and the claim-link channel",
                        "example": "john@example.com"
                      },
                      "phone": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Recipient phone in E.164 format. Used as an additional pin match key",
                        "example": "+4512345678"
                      },
                      "locale": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Locale used for the recipient-facing claim email",
                        "example": "da-DK"
                      },
                      "platform": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Free-text name of the platform the request comes from, for your own reporting (for example `woocommerce`); there is no fixed list",
                        "example": "woocommerce"
                      },
                      "idempotency_key": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "Merchant-side unique key (e.g. order id). Unique per account; replays return the original request",
                        "example": "order-42"
                      },
                      "country": {
                        "type": "string",
                        "description": "ISO 3166-1 alpha-2 country code of the shipping address. The address fields below are optional for every country here, unlike POST /v1/pins",
                        "example": "DE"
                      },
                      "address_line_1": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "Example Street 1"
                      },
                      "address_line_2": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "Suite 2"
                      },
                      "zip_code": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "10115"
                      },
                      "city": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "Berlin"
                      },
                      "state": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "example": "BE"
                      }
                    },
                    "required": [
                      "name",
                      "email",
                      "country"
                    ]
                  }
                },
                "required": [
                  "data"
                ]
              }
            }
          }
        }
      }
    },
    "/v1/usage/alerts": {
      "get": {
        "summary": "Get API quota usage status",
        "tags": [
          "Usage & Quotas"
        ],
        "description": "Returns the current API quota usage status for the authenticated account.\nThis endpoint provides real-time information about monthly and daily quota usage,\nincluding percentages that can be used to display alerts in your UI (e.g., when usage >= 80% or >= 100%).\n\nThis endpoint does not consume quota - checking your usage is free.\n",
        "responses": {
          "200": {
            "description": "successful - no subscription",
            "content": {
              "application/json": {
                "examples": {
                  "active_subscription_example": {
                    "value": {
                      "data": {
                        "has_active_subscription": true,
                        "allowed": true,
                        "reset_at": "2026-03-31T23:59:59Z",
                        "monthly": {
                          "usage": 8500,
                          "limit": 10000,
                          "remaining": 1500,
                          "limit_with_grace": 11500,
                          "remaining_with_grace": 3000,
                          "percentage": 85.0,
                          "in_grace_period": false
                        },
                        "daily": {
                          "usage": 850,
                          "limit": 1000,
                          "remaining": 150,
                          "limit_with_grace": 1150,
                          "remaining_with_grace": 300,
                          "percentage": 85.0,
                          "in_grace_period": false
                        }
                      }
                    }
                  },
                  "no_subscription_example": {
                    "value": {
                      "data": {
                        "has_active_subscription": false,
                        "allowed": false,
                        "message": "No active API subscription. Please sign up for API access."
                      }
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "properties": {
                        "has_active_subscription": {
                          "type": "boolean",
                          "description": "Whether the account has an active API subscription"
                        },
                        "allowed": {
                          "type": "boolean",
                          "description": "Whether API requests are currently allowed"
                        },
                        "reset_at": {
                          "type": "string",
                          "format": "date-time",
                          "description": "ISO 8601 timestamp when the monthly quota will reset"
                        },
                        "monthly": {
                          "$ref": "#/components/schemas/QuotaPeriodUsage"
                        },
                        "daily": {
                          "$ref": "#/components/schemas/QuotaPeriodUsage"
                        },
                        "message": {
                          "type": "string",
                          "description": "Message explaining the subscription status"
                        }
                      },
                      "required": [
                        "has_active_subscription",
                        "allowed",
                        "message"
                      ]
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "pin.address_updated": {
      "post": {
        "summary": "pin.address_updated webhook",
        "security": [],
        "description": "Sent to the webhook endpoint URL registered for the account in the VayaPin dashboard\n(Developer > VayaCore > Webhooks). Sent when a POST /v1/requests request resolves (immediately\non the express path, otherwise once the recipient completes their pin) and again whenever the\nlinked pin's address changes later.\n\nThe body is signed with HMAC-SHA256 over the exact raw request body, using the endpoint's\nsigning secret shown in the dashboard when the webhook is created. The signature is sent as\n`X-VayaPin-Signature: sha256=<hex>`; compare it in constant time before trusting the payload.\n\nRespond with any `2xx` to acknowledge. `5xx` responses and timeouts are retried (up to\n5 attempts) with the same `event_id`, so deduplicate on it; `4xx` responses are not retried.\n",
        "parameters": [
          {
            "name": "X-VayaPin-Signature",
            "in": "header",
            "required": true,
            "description": "HMAC-SHA256 of the raw body keyed with the endpoint signing secret, formatted as `sha256=<hex>`",
            "schema": {
              "type": "string",
              "example": "sha256=3f1c9b..."
            }
          },
          {
            "name": "X-VayaPin-Event-Id",
            "in": "header",
            "required": true,
            "description": "Same value as `event_id` in the body; stable across retries",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Unique event identifier; reused on retries of the same event"
                  },
                  "event_type": {
                    "type": "string",
                    "enum": [
                      "pin.address_updated"
                    ]
                  },
                  "occurred_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "request_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "`data.request_id` returned by POST /v1/requests"
                  },
                  "vayapin_id": {
                    "type": "string",
                    "example": "DK:LARSEN12"
                  },
                  "scripts": {
                    "type": "object",
                    "description": "Formatted one-line address per language. `latin` (English) is always present; `native` is added when the country's default language is not English and a native address could be resolved (e.g. Danish for DK).",
                    "properties": {
                      "latin": {
                        "$ref": "#/components/schemas/WebhookAddressScript"
                      },
                      "native": {
                        "$ref": "#/components/schemas/WebhookAddressScript"
                      }
                    },
                    "required": [
                      "latin"
                    ]
                  },
                  "gps": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "lat": {
                        "type": "number",
                        "format": "float",
                        "example": 55.6761
                      },
                      "lng": {
                        "type": "number",
                        "format": "float",
                        "example": 12.5683
                      }
                    }
                  },
                  "qrcode": {
                    "type": "object",
                    "properties": {
                      "img_url": {
                        "type": "string",
                        "format": "uri"
                      }
                    }
                  },
                  "contact": {
                    "type": "object",
                    "description": "Recipient contact details stored on the pin, for reconciliation",
                    "properties": {
                      "email": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "phone": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "name": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    }
                  }
                },
                "required": [
                  "event_id",
                  "event_type",
                  "occurred_at",
                  "request_id",
                  "vayapin_id",
                  "scripts",
                  "qrcode",
                  "contact"
                ]
              },
              "example": {
                "event_id": "0b6c2f4e-8d1a-4c3b-9e7f-1a2b3c4d5e6f",
                "event_type": "pin.address_updated",
                "occurred_at": "2026-10-08T12:34:56Z",
                "request_id": "c6f8b1d9-7b0e-4d1f-8b2a-7f2b9e88d2b3",
                "vayapin_id": "DK:LARSEN12",
                "scripts": {
                  "latin": {
                    "label": "Vesterbrogade 1, 1620 København V, Danmark",
                    "display": "Vesterbrogade 1, 1620 København V, Danmark"
                  }
                },
                "gps": {
                  "lat": 55.6761,
                  "lng": 12.5683
                },
                "qrcode": {
                  "img_url": "https://api.vayapin.com/r/qrc/p/DK:LARSEN12/webapp/DK_LARSEN12.png"
                },
                "contact": {
                  "email": "lars@example.com",
                  "phone": "+4512345678",
                  "name": "Lars Larsen"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any 2xx acknowledges the event"
          },
          "401": {
            "description": "Signature rejected; not retried"
          },
          "500": {
            "description": "Receiver error; retried with the same event_id"
          }
        },
        "servers": [
          {
            "url": "https://yourstore.example.com/webhooks/vayapin",
            "description": "Your webhook endpoint"
          }
        ]
      }
    }
  },
  "servers": [
    {
      "url": "https://api.vayapin.com",
      "description": "Production server"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Bearer token authentication. Use the token provided after creating an API token."
      }
    },
    "schemas": {
      "PinDataField": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "encrypted": {
            "type": "boolean",
            "description": "Indicates whether the field value is encrypted and not accessible"
          },
          "value": {
            "type": [
              "string",
              "null"
            ],
            "description": "The field value. Null if encrypted or not available"
          }
        },
        "required": [
          "encrypted"
        ]
      },
      "QuotaPeriodUsage": {
        "type": "object",
        "properties": {
          "usage": {
            "type": "integer",
            "description": "Current usage count for the period"
          },
          "limit": {
            "type": "integer",
            "description": "Quota limit for the period"
          },
          "remaining": {
            "type": "integer",
            "description": "Remaining quota for the period"
          },
          "limit_with_grace": {
            "type": "integer",
            "description": "Quota limit including grace percentage"
          },
          "remaining_with_grace": {
            "type": "integer",
            "description": "Remaining quota including grace allowance"
          },
          "percentage": {
            "type": "number",
            "format": "float",
            "description": "Percentage of quota used (0-100). Use this to show alerts (e.g., >= 80 or >= 100)"
          },
          "in_grace_period": {
            "type": "boolean",
            "description": "Whether currently in grace period (usage exceeded limit but within grace)"
          }
        },
        "required": [
          "usage",
          "limit",
          "remaining",
          "limit_with_grace",
          "remaining_with_grace",
          "percentage",
          "in_grace_period"
        ]
      },
      "QuotaExceededErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code",
                "example": "usage_limit_exceeded"
              },
              "message": {
                "type": "string",
                "description": "Human-readable error message",
                "example": "API quota exceeded. Monthly: 1100/1000, Daily: 110/100"
              },
              "current_usage": {
                "type": "integer",
                "description": "Current usage count"
              },
              "limit": {
                "type": "integer",
                "description": "Quota limit"
              },
              "reset_time": {
                "type": "string",
                "format": "date-time",
                "description": "When the quota resets (ISO 8601 format)"
              },
              "retry_after": {
                "type": "integer",
                "description": "Seconds until quota resets"
              },
              "request_id": {
                "type": "string",
                "description": "Unique request identifier for troubleshooting"
              },
              "backtrace": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Stack trace (only in development/test environments)"
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": true
          }
        },
        "required": [
          "error"
        ]
      },
      "WebhookAddressScript": {
        "type": "object",
        "properties": {
          "label": {
            "type": [
              "string",
              "null"
            ],
            "description": "Address formatted in English; null when the address could not be geocoded"
          },
          "display": {
            "type": [
              "string",
              "null"
            ],
            "description": "Address formatted in the key's language; null when the address could not be geocoded"
          }
        },
        "required": [
          "label",
          "display"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code"
              },
              "message": {
                "type": "string",
                "description": "Human-readable error message"
              },
              "request_id": {
                "type": "string",
                "description": "Unique request identifier for troubleshooting"
              },
              "backtrace": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Stack trace (only in development/test environments)"
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ]
      }
    }
  }
}
