Skip to main content

API reference

VayaPin API version 1.0.0.

Base URLs:

  • https://api.vayapin.com — Production server

Every endpoint needs Authorization: Bearer YOUR_API_TOKEN. See Getting started for tokens, error shapes and quotas, and Receiving webhooks for the pin.address_updated event.

Interactive version

/api-reference renders the same spec with a request builder. This page is the readable version. The spec itself is at /openapi.json.

POST /v1/requests​

Ask VayaPin for a recipient's address on behalf of a merchant (VayaConnect store plugins).

The account must have an enabled webhook endpoint provisioned before it can create requests; the resolved address is always delivered to that endpoint, never in this response.

Two outcomes are possible, both returned as 201 Created and distinguished by data.status:

  • resolved (express path) - the recipient's email or phone already has a pin. The pin is linked to the request and the address webhook is dispatched immediately.
  • pending - no pin matched, so a claimable pin is created and the recipient is emailed a claim link. The webhook fires later, once the recipient completes or updates their pin.

Send idempotency_key (for example the merchant's order id) to make retries safe: a repeat call with a key already used by the account returns the original request with 200 OK instead of creating a second one. The key is read from the request body only; an Idempotency-Key HTTP header is ignored.

Phone-only requests are not supported yet - email is required.

The resolved address is delivered as a pin.address_updated webhook to the account's webhook endpoint (set up in the VayaPin dashboard under Developer > VayaCore > Webhooks) - see Webhooks.

Request body (application/json)​

FieldTypeRequiredDescription
dataobjectYes—
data.namestringYesFull name of the recipient (first + last), used to derive the pin name Example: John Example.
data.first_name['string', 'null']NoFirst name (overrides the split from name) Example: John.
data.last_name['string', 'null']NoLast name (overrides the split from name) Example: Example.
data.emailstring (email)YesRecipient email. Required - it is both the pin match key and the claim-link channel Example: john@example.com.
data.phone['string', 'null']NoRecipient phone in E.164 format. Used as an additional pin match key Example: +4512345678.
data.locale['string', 'null']NoLocale used for the recipient-facing claim email Example: da-DK.
data.platform['string', 'null']NoFree-text name of the platform the request comes from, for your own reporting (for example woocommerce); there is no fixed list Example: woocommerce.
data.idempotency_key['string', 'null']NoMerchant-side unique key (e.g. order id). Unique per account; replays return the original request Example: order-42.
data.countrystringYesISO 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.
data.address_line_1['string', 'null']NoExample: Example Street 1.
data.address_line_2['string', 'null']NoExample: Suite 2.
data.zip_code['string', 'null']NoExample: 10115.
data.city['string', 'null']NoExample: Berlin.
data.state['string', 'null']NoExample: BE.

Responses​

StatusDescription
200request replayed (idempotency key already used)
201request created
400body is not wrapped in data
401unauthorized
422request rejected
429quota exceeded

POST /v1/pins​

Create a new pin by provisioning an account, subscription request, and data set update.

The full VayaPin identifier of the new pin is data.account.default_country + : + data.pin_name (for example DE:EXAMPLE).

A 207 Multi-Status response means the pin was created but some sections of its data set could not be saved; data.pin_data_set_update.sections reports the result of every section.

Required address fields depend on country. Most countries need address_line_1, zip_code and city; some need fewer or also state, and some check the zip_code format. A missing or malformed field is reported in data.errors of the 422 response, for example "city": ["is required for UA addresses"].

Request body (application/json)​

FieldTypeRequiredDescription
dataobjectYes—
data.countrystringYesISO 3166-1 alpha-2 country code Example: DE.
data.namestringYesFull name (first + last), used to derive pin name Example: John Example.
data.first_name['string', 'null']NoFirst name (overrides split from name) Example: John.
data.last_name['string', 'null']NoLast name (overrides split from name) Example: Example.
data.emailstring (email)YesExample: john@example.com.
data.phone_number['string', 'null']NoExample: +1234567890.
data.address_line_1['string', 'null']NoStreet and number Example: Example Street 1.
data.address_line_2['string', 'null']NoExample: Suite 2.
data.zip_code['string', 'null']NoExample: 10115.
data.city['string', 'null']NoExample: Berlin.
data.state['string', 'null']NoExample: BE.

Responses​

StatusDescription
200pin created
207pin created, data set partially saved
401unauthorized
422validation error - invalid input
429too many requests

GET /v1/pins/{id}​

Retrieve pin data by its VayaPin identifier. Returns data based on account abilities and excludes password-protected fields.

Parameters​

NameInRequiredDescription
idpathYesVayaPin identifier (e.g., DK:EXAMPLE)

Responses​

StatusDescription
200successful
401unauthorized
403forbidden - expired subscription
404not found
429too many requests

GET /v1/usage/alerts​

Get API quota usage status

Returns the current API quota usage status for the authenticated account. This endpoint provides real-time information about monthly and daily quota usage, including percentages that can be used to display alerts in your UI (e.g., when usage >= 80% or >= 100%).

This endpoint does not consume quota - checking your usage is free.

Responses​

StatusDescription
200successful - no subscription
401unauthorized

Webhooks​

Events VayaPin sends to your registered endpoint. See Receiving webhooks for verification and retry handling.

pin.address_updated​

Sent to the webhook endpoint URL registered for the account in the VayaPin dashboard (Developer > VayaCore > Webhooks). Sent when a POST /v1/requests request resolves (immediately on the express path, otherwise once the recipient completes their pin) and again whenever the linked pin's address changes later.

The body is signed with HMAC-SHA256 over the exact raw request body, using the endpoint's signing secret shown in the dashboard when the webhook is created. The signature is sent as X-VayaPin-Signature: sha256=<hex>; compare it in constant time before trusting the payload.

Respond with any 2xx to acknowledge. 5xx responses and timeouts are retried (up to 5 attempts) with the same event_id, so deduplicate on it; 4xx responses are not retried.

Headers​

HeaderDescription
X-VayaPin-SignatureHMAC-SHA256 of the raw body keyed with the endpoint signing secret, formatted as sha256=<hex>
X-VayaPin-Event-IdSame value as event_id in the body; stable across retries

Payload (application/json)​

FieldTypeRequiredDescription
event_idstring (uuid)YesUnique event identifier; reused on retries of the same event
event_typestringYes—
occurred_atstring (date-time)Yes—
request_idstring (uuid)Yesdata.request_id returned by POST /v1/requests
vayapin_idstringYesExample: DK:LARSEN12.
scriptsobjectYesFormatted 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).
scripts.latinobjectYes—
scripts.latin.label['string', 'null']YesAddress formatted in English; null when the address could not be geocoded
scripts.latin.display['string', 'null']YesAddress formatted in the key's language; null when the address could not be geocoded
scripts.nativeobjectNo—
scripts.native.label['string', 'null']YesAddress formatted in English; null when the address could not be geocoded
scripts.native.display['string', 'null']YesAddress formatted in the key's language; null when the address could not be geocoded
gps['object', 'null']No—
qrcodeobjectYes—
qrcode.img_urlstring (uri)No—
contactobjectYesRecipient contact details stored on the pin, for reconciliation
contact.email['string', 'null']No—
contact.phone['string', 'null']No—
contact.name['string', 'null']No—
{
"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\u00f8benhavn V, Danmark",
"display": "Vesterbrogade 1, 1620 K\u00f8benhavn 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"
}
}