Справочник API

Сгенерировано из закреплённой спецификации OpenAPI 1.0.0. Это релизный контракт, не подтверждение версии публичного API: перед включением возможностей проверьте GET /v1/capabilities своего контура. Описания запросов — на английском, как в спецификации. openapi.yaml · llms-full.txt

Первый стабильный контракт API 1.0.0, включая дополнительные услуги. Исходный коммит Net: da2b28b41924. Версии релизов Site и Via независимы от версии контракта API.

Для подтверждения заказа, даже нулевого, нужен отдельный X-Commerce-Key. Подключение рабочего партнёра явное; до него сохраняется старый контракт суперлайков и отмен. Услуги и порядок подключения

Доступ

Адресhttps://api.pereval.dating — для тестового и боевого контуров, контур определяет ключ
АвторизацияAuthorization: Bearer <api_key> — только с вашего сервера
ФорматJSON, UTF-8, время RFC 3339 UTC
Ошибки{"error": "<code>", "message": "…"} — все коды
Лимит600 запросов в минуту на площадку; лента — 200 анкет на участника в сутки

Участники

Push, update and remove your users' network profiles.

Add or update members in a batch

PUT/v1/members

Up to 100 members per request, each validated on its own — one bad profile doesn't block the rest. Use it for the first upload of users who turned the network on.

curl -X PUT 'https://api.pereval.dating/v1/members' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "external_id": "string",
      "consent": {
        "network": true,
        "cross_language": false,
        "world": false,
        "given_at": "2026-10-24T12:00:00Z",
        "text_version": "string"
      },
      "display_name": "string",
      "birth_year": 1,
      "gender": "male",
      "age_min": 18,
      "age_max": 1,
      "languages": [
        "string"
      ],
      "city": {
        "name": "string",
        "country": "string",
        "lat": 1.5,
        "lon": 1.5
      },
      "bio": "string",
      "photos": [
        "https://example.com/photo.jpg"
      ],
      "videos": [
        {
          "url": "https://example.com/photo.jpg",
          "poster": "https://example.com/photo.jpg"
        }
      ],
      "photo_verified": false,
      "verification": {
        "method": "selfie",
        "provider": "string",
        "verified_at": "2026-10-24T12:00:00Z",
        "check_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
      },
      "intents": [
        "serious",
        "casual",
        "friendship"
      ],
      "arrangement_role": "provider",
      "last_active_at": "2026-10-24T12:00:00Z",
      "height_cm": 120,
      "show_distance": false
    }
  ]
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/members');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'items' => [[
            'external_id' => 'string',
            'consent' => [
                'network' => true,
                'cross_language' => false,
                'world' => false,
                'given_at' => '2026-10-24T12:00:00Z',
                'text_version' => 'string',
            ],
            'display_name' => 'string',
            'birth_year' => 1,
            'gender' => 'male',
            'age_min' => 18,
            'age_max' => 1,
            'languages' => ['string'],
            'city' => [
                'name' => 'string',
                'country' => 'string',
                'lat' => 1.5,
                'lon' => 1.5,
            ],
            'bio' => 'string',
            'photos' => ['https://example.com/photo.jpg'],
            'videos' => [[
                'url' => 'https://example.com/photo.jpg',
                'poster' => 'https://example.com/photo.jpg',
            ]],
            'photo_verified' => false,
            'verification' => [
                'method' => 'selfie',
                'provider' => 'string',
                'verified_at' => '2026-10-24T12:00:00Z',
                'check_id' => '6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44',
            ],
            'intents' => ['serious', 'casual', 'friendship'],
            'arrangement_role' => 'provider',
            'last_active_at' => '2026-10-24T12:00:00Z',
            'height_cm' => 120,
            'show_distance' => false,
        ]],
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.put(
    "https://api.pereval.dating/v1/members",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "items": [
            {
                "external_id": "string",
                "consent": {
                    "network": True,
                    "cross_language": False,
                    "world": False,
                    "given_at": "2026-10-24T12:00:00Z",
                    "text_version": "string"
                },
                "display_name": "string",
                "birth_year": 1,
                "gender": "male",
                "age_min": 18,
                "age_max": 1,
                "languages": [
                    "string"
                ],
                "city": {
                    "name": "string",
                    "country": "string",
                    "lat": 1.5,
                    "lon": 1.5
                },
                "bio": "string",
                "photos": [
                    "https://example.com/photo.jpg"
                ],
                "videos": [
                    {
                        "url": "https://example.com/photo.jpg",
                        "poster": "https://example.com/photo.jpg"
                    }
                ],
                "photo_verified": False,
                "verification": {
                    "method": "selfie",
                    "provider": "string",
                    "verified_at": "2026-10-24T12:00:00Z",
                    "check_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
                },
                "intents": [
                    "serious",
                    "casual",
                    "friendship"
                ],
                "arrangement_role": "provider",
                "last_active_at": "2026-10-24T12:00:00Z",
                "height_cm": 120,
                "show_distance": False
            }
        ]
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "items": [
      {
        "external_id": "string",
        "consent": {
          "network": true,
          "cross_language": false,
          "world": false,
          "given_at": "2026-10-24T12:00:00Z",
          "text_version": "string"
        },
        "display_name": "string",
        "birth_year": 1,
        "gender": "male",
        "age_min": 18,
        "age_max": 1,
        "languages": [
          "string"
        ],
        "city": {
          "name": "string",
          "country": "string",
          "lat": 1.5,
          "lon": 1.5
        },
        "bio": "string",
        "photos": [
          "https://example.com/photo.jpg"
        ],
        "videos": [
          {
            "url": "https://example.com/photo.jpg",
            "poster": "https://example.com/photo.jpg"
          }
        ],
        "photo_verified": false,
        "verification": {
          "method": "selfie",
          "provider": "string",
          "verified_at": "2026-10-24T12:00:00Z",
          "check_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
        },
        "intents": [
          "serious",
          "casual",
          "friendship"
        ],
        "arrangement_role": "provider",
        "last_active_at": "2026-10-24T12:00:00Z",
        "height_cm": 120,
        "show_distance": false
      }
    ]
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "items": [
    {
      "external_id": "string",
      "consent": {
        "network": true,
        "cross_language": false,
        "world": false,
        "given_at": "2026-10-24T12:00:00Z",
        "text_version": "string"
      },
      "display_name": "string",
      "birth_year": 1,
      "gender": "male",
      "age_min": 18,
      "age_max": 1,
      "languages": [
        "string"
      ],
      "city": {
        "name": "string",
        "country": "string",
        "lat": 1.5,
        "lon": 1.5
      },
      "bio": "string",
      "photos": [
        "https://example.com/photo.jpg"
      ],
      "videos": [
        {
          "url": "https://example.com/photo.jpg",
          "poster": "https://example.com/photo.jpg"
        }
      ],
      "photo_verified": false,
      "verification": {
        "method": "selfie",
        "provider": "string",
        "verified_at": "2026-10-24T12:00:00Z",
        "check_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
      },
      "intents": [
        "serious",
        "casual",
        "friendship"
      ],
      "arrangement_role": "provider",
      "last_active_at": "2026-10-24T12:00:00Z",
      "height_cm": 120,
      "show_distance": false
    }
  ]
}`)

req, _ := http.NewRequestWithContext(ctx, "PUT", "https://api.pereval.dating/v1/members", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
itemsобязательно
object[]
1–100 items
external_idобязательно
string
Your user id · 1–128 chars
consentобязательно
object
networkобязательно
boolean
The user enabled "show me in the network". false hides the member (see the upsert) instead of being rejected
cross_language
boolean
The user agreed to chat with people speaking other languages (machine translation) · default false
world
boolean
The user wants to meet people from other cities and countries — the scope=world feed. Opt-in only, off by default · default false
given_atобязательно
string (date-time)
When the user actually turned it on
text_versionобязательно
string
Version of the consent text the user saw, e.g. its date
display_nameобязательно
string
1–50 chars
birth_yearобязательно
integer
Members must be 18+
genderобязательно
"male" | "female"
Matches are always of the opposite gender; there is no "seeking" field (ignored if sent)
age_minобязательно
integer
≥ 18
age_maxобязательно
integer
languagesобязательно
string[]
First is the main language: profiles and messages are translated into it. Languages listed here are never translated for this member. Translation languages — GET /v1/languages (38 in v0.8); other ISO 639-1 codes are accepted, names such as Russian are rejected with 422. · ≥ 1 items
cityобязательно
object
Name or coordinates required. Can change once a day.
name
string
country
string
ISO 3166-1 alpha-2
lat
number
lon
number
bio
string
≤ 1000 chars
photosобязательно
string (uri)[]
Public HTTPS URLs; 1–9 while visible, may be empty while hidden · ≤ 9 items
videos
object[]
Short clips shown on the card next to photos, not instead of them. Omit or send [] to clear. production rejects all nonempty video input with 422 until frame moderation exists. Inspect the videos capability; the following format is for isolated development only. Each clip is an MP4 up to 15 seconds and 20 MB, plus a poster image. The file stays on your host. There is no mute flag: the viewing app starts clips silent and has one switch for every clip. Posters are moderated like photos. A clip that is not an MP4, is longer, or cannot be downloaded rejects the profile (video_invalid, video_too_long, video_unavailable). · ≤ 3 items
urlобязательно
string (uri)
Public MP4, at most 15 seconds and 20 MB
posterобязательно
string (uri)
Still frame. Shown before playback and by apps that do not play video. Moderated like a photo
photo_verified
boolean
Before 0.15 — "you verified the person matches the photos". Still accepted: without verification it is a claim with an unspecified method made when the network receives it. Send verification instead. · default false
verification
object
How and when you checked that the person matches their photos. The network turns it into the photo_verified mark that other apps see, if (1) the network has approved your verification method — describe it when you connect, the network reviews it once — and (2) none of the current photos is newer than verified_at. Photos the person joins the network with are covered by an earlier check too; a photo added later switches the mark off until you send a newer verified_at. The same picture under a new URL is recognised by its content. The network re-checks a sample of verified people and those with fake signals (reports, the same photo on another person, generated images); if your marks fail, the network stops counting them for all your users. Omit (and photo_verified: false) — no claim. — or a claim by the network's photo check: {"provider": "pereval", "check_id": "…"}. The network verifies that the check is yours, has passed and covers all current photos (each with a match or no_face verdict, at least one match, recognised by URL or content); then the mark counts even if your own method isn't approved, and verified_at is the time of the check. method and verified_at are then ignored.
method
"selfie" | "video" | "liveness" | "document" | "in_person" | "other"
selfie — a photo selfie (e.g. with a gesture); video — a video selfie; liveness — an automated liveness check; document — an ID with a face match; in_person — met in person
provider
string
Who did the check: a vendor (e.g. Sumsub), own, or pereval — the network's photo check (with check_id) · ≤ 60 chars
verified_at
string (date-time)
When the check passed. Not in the future.
check_id
string (uuid)
id of a passed photo-check session of yours, less than 60 days from original creation. An expired/deleted reference returns 422 invalid; do not retry it or automatically create a replacement check. Omit a stale reference on subsequent upserts. Already accepted verification of unchanged photos is independent of case material retention; expiry does not approve any new photos.
intents
"serious" | "casual" | "friendship" | "arrangement"[]
What the member is looking for. Two members are matched only if they share an intent. arrangement — a relationship with a well-off partner — is opt-in only, requires your app to have the category enabled, and requires arrangement_role. Members whose only shared intent is arrangement are matched only with the opposite role. Offering money for sex is forbidden in every category. · default ["serious","casual","friendship"]
arrangement_role
"provider" | "seeker"
With the arrangement intent: provider — ready to support a partner; seeker — looking for a well-off partner
last_active_at
string (date-time)
height_cm
integer
Height in cm · 120–230
show_distance
boolean
Others may see distance_km to this person. Off by default. Does not change the city. · default false

Ответы

200 Per-item results in the same order
ПолеОписание
itemsобязательно
object[]
external_idобязательно
string
network_id
string (uuid)
Present when stored
photo_verified
boolean
Whether the verification mark counts — see the single upsert
verification
"accepted" | "none" | "no_photos" | "partner_not_approved" | "photos_changed"
Why the verification mark counts or not: accepted; none — no claim; no_photos; partner_not_approved — the network hasn't approved your verification method yet (never for claims by the network's photo check, ); photos_changed — a photo is newer than verified_at: check the person again and send a newer verified_at.
error
object
Present when this item was rejected
code
"invalid" | "location_change_limited"
message
string
422 Validation failed; message says which field

Add or update a member

PUT/v1/members/{external_id}

Idempotent upsert by external_id. Call it when a user opts in and whenever their profile, photos, preferences or consents change.

Hidden members. consent.network: false hides the member instead of removing them: the same network_id, matches, conversations, dislikes and blocks stay; the member is not in anyone's feed and gets no feed (409 member_hidden); they can only like back someone who already liked them (that is a match), no super likes. Messages in existing matches flow both ways. Photos are optional while hidden. Send consent.network: true again and the member is visible with all their history. Use this when the user turns the network off; use DELETE only to erase their network data (an explicit "delete my data from the network" or account deletion). Coordinates are rounded to 0.01° (~1 km) by the network; send city-level precision anyway. The city may change once a day: a new city (or a move over 50 km) sooner than 24 h after the previous change returns 409 location_change_limited. The bio's language is detected by the network; the bio is translated on demand into the main languages of members who are reading the feed.

Moderation. A new profile, or one with changed photos, name or bio, is checked before it appears in feeds — usually within seconds. Explicit photos, insults or obscenity, contact details (phone, link, messenger handle) and photos that cannot be downloaded are not allowed. On rejection you get member.rejected with the reason; the profile is hidden and cannot like until you send a corrected one (resending the same profile does not trigger a new check). Photos and text that already passed are not checked again.

curl -X PUT 'https://api.pereval.dating/v1/members/user-123' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "consent": {
    "network": true,
    "cross_language": true,
    "world": false,
    "given_at": "2026-10-24T12:00:00Z",
    "text_version": "2026-10-24"
  },
  "display_name": "Anna",
  "birth_year": 1994,
  "gender": "female",
  "age_min": 28,
  "age_max": 40,
  "languages": [
    "ru",
    "en"
  ],
  "city": {
    "name": "Moscow",
    "country": "RU",
    "lat": 55.75,
    "lon": 37.62
  },
  "bio": "Hiking, jazz and good coffee",
  "photos": [
    "https://cdn.example-dating.app/u/123/1.jpg"
  ],
  "photo_verified": true,
  "height_cm": 168
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'consent' => [
            'network' => true,
            'cross_language' => true,
            'world' => false,
            'given_at' => '2026-10-24T12:00:00Z',
            'text_version' => '2026-10-24',
        ],
        'display_name' => 'Anna',
        'birth_year' => 1994,
        'gender' => 'female',
        'age_min' => 28,
        'age_max' => 40,
        'languages' => ['ru', 'en'],
        'city' => [
            'name' => 'Moscow',
            'country' => 'RU',
            'lat' => 55.75,
            'lon' => 37.62,
        ],
        'bio' => 'Hiking, jazz and good coffee',
        'photos' => ['https://cdn.example-dating.app/u/123/1.jpg'],
        'photo_verified' => true,
        'height_cm' => 168,
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.put(
    "https://api.pereval.dating/v1/members/user-123",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "consent": {
            "network": True,
            "cross_language": True,
            "world": False,
            "given_at": "2026-10-24T12:00:00Z",
            "text_version": "2026-10-24"
        },
        "display_name": "Anna",
        "birth_year": 1994,
        "gender": "female",
        "age_min": 28,
        "age_max": 40,
        "languages": [
            "ru",
            "en"
        ],
        "city": {
            "name": "Moscow",
            "country": "RU",
            "lat": 55.75,
            "lon": 37.62
        },
        "bio": "Hiking, jazz and good coffee",
        "photos": [
            "https://cdn.example-dating.app/u/123/1.jpg"
        ],
        "photo_verified": True,
        "height_cm": 168
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members/user-123", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "consent": {
      "network": true,
      "cross_language": true,
      "world": false,
      "given_at": "2026-10-24T12:00:00Z",
      "text_version": "2026-10-24"
    },
    "display_name": "Anna",
    "birth_year": 1994,
    "gender": "female",
    "age_min": 28,
    "age_max": 40,
    "languages": [
      "ru",
      "en"
    ],
    "city": {
      "name": "Moscow",
      "country": "RU",
      "lat": 55.75,
      "lon": 37.62
    },
    "bio": "Hiking, jazz and good coffee",
    "photos": [
      "https://cdn.example-dating.app/u/123/1.jpg"
    ],
    "photo_verified": true,
    "height_cm": 168
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "consent": {
    "network": true,
    "cross_language": true,
    "world": false,
    "given_at": "2026-10-24T12:00:00Z",
    "text_version": "2026-10-24"
  },
  "display_name": "Anna",
  "birth_year": 1994,
  "gender": "female",
  "age_min": 28,
  "age_max": 40,
  "languages": [
    "ru",
    "en"
  ],
  "city": {
    "name": "Moscow",
    "country": "RU",
    "lat": 55.75,
    "lon": 37.62
  },
  "bio": "Hiking, jazz and good coffee",
  "photos": [
    "https://cdn.example-dating.app/u/123/1.jpg"
  ],
  "photo_verified": true,
  "height_cm": 168
}`)

req, _ := http.NewRequestWithContext(ctx, "PUT", "https://api.pereval.dating/v1/members/user-123", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.

Тело запроса

ПолеОписание
consentобязательно
object
networkобязательно
boolean
The user enabled "show me in the network". false hides the member (see the upsert) instead of being rejected
cross_language
boolean
The user agreed to chat with people speaking other languages (machine translation) · default false
world
boolean
The user wants to meet people from other cities and countries — the scope=world feed. Opt-in only, off by default · default false
given_atобязательно
string (date-time)
When the user actually turned it on
text_versionобязательно
string
Version of the consent text the user saw, e.g. its date
display_nameобязательно
string
1–50 chars
birth_yearобязательно
integer
Members must be 18+
genderобязательно
"male" | "female"
Matches are always of the opposite gender; there is no "seeking" field (ignored if sent)
age_minобязательно
integer
≥ 18
age_maxобязательно
integer
languagesобязательно
string[]
First is the main language: profiles and messages are translated into it. Languages listed here are never translated for this member. Translation languages — GET /v1/languages (38 in v0.8); other ISO 639-1 codes are accepted, names such as Russian are rejected with 422. · ≥ 1 items
cityобязательно
object
Name or coordinates required. Can change once a day.
name
string
country
string
ISO 3166-1 alpha-2
lat
number
lon
number
bio
string
≤ 1000 chars
photosобязательно
string (uri)[]
Public HTTPS URLs; 1–9 while visible, may be empty while hidden · ≤ 9 items
videos
object[]
Short clips shown on the card next to photos, not instead of them. Omit or send [] to clear. production rejects all nonempty video input with 422 until frame moderation exists. Inspect the videos capability; the following format is for isolated development only. Each clip is an MP4 up to 15 seconds and 20 MB, plus a poster image. The file stays on your host. There is no mute flag: the viewing app starts clips silent and has one switch for every clip. Posters are moderated like photos. A clip that is not an MP4, is longer, or cannot be downloaded rejects the profile (video_invalid, video_too_long, video_unavailable). · ≤ 3 items
urlобязательно
string (uri)
Public MP4, at most 15 seconds and 20 MB
posterобязательно
string (uri)
Still frame. Shown before playback and by apps that do not play video. Moderated like a photo
photo_verified
boolean
Before 0.15 — "you verified the person matches the photos". Still accepted: without verification it is a claim with an unspecified method made when the network receives it. Send verification instead. · default false
verification
object
How and when you checked that the person matches their photos. The network turns it into the photo_verified mark that other apps see, if (1) the network has approved your verification method — describe it when you connect, the network reviews it once — and (2) none of the current photos is newer than verified_at. Photos the person joins the network with are covered by an earlier check too; a photo added later switches the mark off until you send a newer verified_at. The same picture under a new URL is recognised by its content. The network re-checks a sample of verified people and those with fake signals (reports, the same photo on another person, generated images); if your marks fail, the network stops counting them for all your users. Omit (and photo_verified: false) — no claim. — or a claim by the network's photo check: {"provider": "pereval", "check_id": "…"}. The network verifies that the check is yours, has passed and covers all current photos (each with a match or no_face verdict, at least one match, recognised by URL or content); then the mark counts even if your own method isn't approved, and verified_at is the time of the check. method and verified_at are then ignored.
method
"selfie" | "video" | "liveness" | "document" | "in_person" | "other"
selfie — a photo selfie (e.g. with a gesture); video — a video selfie; liveness — an automated liveness check; document — an ID with a face match; in_person — met in person
provider
string
Who did the check: a vendor (e.g. Sumsub), own, or pereval — the network's photo check (with check_id) · ≤ 60 chars
verified_at
string (date-time)
When the check passed. Not in the future.
check_id
string (uuid)
id of a passed photo-check session of yours, less than 60 days from original creation. An expired/deleted reference returns 422 invalid; do not retry it or automatically create a replacement check. Omit a stale reference on subsequent upserts. Already accepted verification of unchanged photos is independent of case material retention; expiry does not approve any new photos.
intents
"serious" | "casual" | "friendship" | "arrangement"[]
What the member is looking for. Two members are matched only if they share an intent. arrangement — a relationship with a well-off partner — is opt-in only, requires your app to have the category enabled, and requires arrangement_role. Members whose only shared intent is arrangement are matched only with the opposite role. Offering money for sex is forbidden in every category. · default ["serious","casual","friendship"]
arrangement_role
"provider" | "seeker"
With the arrangement intent: provider — ready to support a partner; seeker — looking for a well-off partner
last_active_at
string (date-time)
height_cm
integer
Height in cm · 120–230
show_distance
boolean
Others may see distance_km to this person. Off by default. Does not change the city. · default false

Ответы

200 Stored
ПолеОписание
network_idобязательно
string (uuid)
The member's id in the network. Stable across updates.
photo_verified
boolean
Whether the verification mark counts: other apps see it and verified_only keeps the person
verification
"accepted" | "none" | "no_photos" | "partner_not_approved" | "photos_changed"
Why the verification mark counts or not: accepted; none — no claim; no_photos; partner_not_approved — the network hasn't approved your verification method yet (never for claims by the network's photo check, ); photos_changed — a photo is newer than verified_at: check the person again and send a newer verified_at.
400 Body is not valid JSON
401 Missing, invalid or suspended API key
409 location_change_limited — the city can change once a day
422 Validation failed; message says which field
429 Over 600 requests per minute

Erase a member from the network

DELETE/v1/members/{external_id}

Call when the user deletes their account or asks to delete their data from the network (to just turn the network off, send consent.network: false — see the upsert). Their open matches are closed: the other platforms get match.closed with reason (account_deleted or left_network) and peer_deleted: true — show the chat as "Account deleted" / "Left the network", read-only. the messages stay with the other person (like Telegram); the profile (name, photos, bio, city), likes, dislikes and blocks are erased. If the user comes back, it is a new member.

curl -X DELETE 'https://api.pereval.dating/v1/members/user-123' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.delete(
    "https://api.pereval.dating/v1/members/user-123",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/members/user-123", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
req, _ := http.NewRequestWithContext(ctx, "DELETE", "https://api.pereval.dating/v1/members/user-123", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.
reason
query · "left_network" | "account_deleted"
account_deleted or left_network (default) — goes to the other side in match.closed

Ответы

204 Removed
401 Missing, invalid or suspended API key
404 Member, match or message not found (or not yours)

The user is deleting their account (can restore within 30 days)

POST/v1/members/{external_id}/deactivate

Deferred account deletion: the member disappears from feeds and "liked you", their matches are frozen — the other platforms get match.frozen and should show "Account deleted"; nobody can write (409 match_frozen). Nothing is erased. Call /restore if the user comes back, or DELETE /v1/members/{external_id}?reason=account_deleted when the 30 days are over; if you don't, the network erases the member itself after 30 days.

curl -X POST 'https://api.pereval.dating/v1/members/user-123/deactivate' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/deactivate');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/members/user-123/deactivate",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/deactivate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/members/user-123/deactivate", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.

Ответы

204 Deleting (idempotent)
403 Not allowed (e.g. liking or disliking a member of your own app, or a suspended member)
404 Member, match or message not found (or not yours)

Bring back a member the network hid after reports

POST/v1/members/{external_id}/reinstate

Your moderator reviewed the reports (member.reported) and found nothing wrong. The open reports are closed, the member is back in feeds and their frozen matches resume (match.unfrozen to the other platforms). A member who is deleting their account can't be reinstated (403).

curl -X POST 'https://api.pereval.dating/v1/members/user-123/reinstate' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/reinstate');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/members/user-123/reinstate",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/reinstate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/members/user-123/reinstate", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.

Ответы

204 Reinstated (idempotent)
403 Not allowed (e.g. liking or disliking a member of your own app, or a suspended member)
404 Member, match or message not found (or not yours)

The user restored their account

POST/v1/members/{external_id}/restore

Everything is back as it was; the frozen matches resume (match.unfrozen to the other platforms).

curl -X POST 'https://api.pereval.dating/v1/members/user-123/restore' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/restore');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/members/user-123/restore",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/restore", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/members/user-123/restore", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.

Ответы

204 Restored (idempotent)
403 Not allowed (e.g. liking or disliking a member of your own app, or a suspended member)
404 Member, match or message not found (or not yours)

Mark a member as active now

POST/v1/members/{external_id}/active

Feeds prefer recently active people and hide those inactive for 30+ days. For many users use POST /v1/activity.

curl -X POST 'https://api.pereval.dating/v1/members/user-123/active' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/active');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/members/user-123/active",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/active", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/members/user-123/active", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.

Ответы

204 Updated
404 Member, match or message not found (or not yours)

Mark many members as active

POST/v1/activity

Up to 1000 external ids — e.g. everyone who opened the app in the last few minutes. Unknown ids are skipped.

curl -X POST 'https://api.pereval.dating/v1/activity' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "external_ids": [
    "string"
  ]
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/activity');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'external_ids' => ['string'],
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/activity",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "external_ids": [
            "string"
        ]
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/activity", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "external_ids": [
      "string"
    ]
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "external_ids": [
    "string"
  ]
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/activity", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
external_idsобязательно
string[]
1–1000 items

Ответы

200 How many members were found and updated
ПолеОписание
updated
integer
422 Validation failed; message says which field

Лента

People from other apps who fit your user.

People from other apps for this member

GET/v1/members/{external_id}/feed

Members of other platforms only (you show your own users yourself). A candidate appears when: opposite gender; each one's age is inside the other's range; within 50 km or radius_km (or the same city and country when coordinates are missing); they share a language, or both consented to cross-language chat; they share an intent; active in the last 30 days; not liked, not disliked in the last 30 days; no block either way. Most recently active first.

Filters only narrow what the viewer's profile already allows. age_min/age_max intersect the viewer's range (an empty intersection is an empty feed and 200, not an error; age_min > age_max in the query is 422). intent must be one of the viewer's intents, or 422. language is an ISO 639-1 code and still requires a shared language or both consents to cross-language chat. gender, if sent, must be the opposite of the viewer, or 422.

Pages use cursor from the previous response's next_cursor. The same request without cursor returns the same people. A member sees at most 200 distinct new people a day (UTC) — remaining_today. Repeating people already shown today does not spend it. At zero, new people stop and people already shown today still come back. A dislike hides a profile for 30 days; it is not how you turn the page. A member hidden after reports or deleting their account gets no feed (403).

bio is translated into the viewer's main language when the viewer doesn't speak its language — show the "translated" mark and let the user see bio_original. Translations are made on demand: the first time a profile is shown to someone with a new main language it may come in the original (bio_translated: false), and later feeds carry the translation.

scope=world drops the distance condition: people from any city and country, but only those with consent.world. The viewer needs that consent too, otherwise 409 world_consent_required. Language and intent conditions still apply. The 200 a day limit is shared by both scopes. Show the city and country on the card.

Viewer filters: radius_km widens or narrows the nearby search (ignored with scope=world); verified_only=true keeps only photo_verified profiles (— marks the network counts, see verification); height_min/height_max keep people within that height — people who didn't specify their height still pass, so a height filter never empties the feed by itself. Store your users' filters yourself and send them with every request.

lat+lon (and optional city/country) search nearby around that point instead of the viewer's profile city — the profile city does not change. distance_km is present only when that person turned on show_distance.

curl -X GET 'https://api.pereval.dating/v1/members/user-123/feed' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "X-Feed-Request-Ref: $PEREVAL_FEED_REQUEST_REF"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/feed');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'X-Feed-Request-Ref: ' . getenv('PEREVAL_FEED_REQUEST_REF'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/members/user-123/feed",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}", "X-Feed-Request-Ref": os.environ['PEREVAL_FEED_REQUEST_REF']},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/feed", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "X-Feed-Request-Ref": process.env.PEREVAL_FEED_REQUEST_REF,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/members/user-123/feed", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("X-Feed-Request-Ref", os.Getenv("PEREVAL_FEED_REQUEST_REF"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.
X-Feed-Request-Ref
header · string
Logical search reference for opt-in boost delivery; preserve across retries/pages, change for a new search.
limit
query · integer
scope
query · "nearby" | "world"
radius_km
query · integer
Search radius for scope=nearby
verified_only
query · boolean
Only people with photo_verified
height_min
query · integer
Minimum height, cm; people without a height pass
height_max
query · integer
Maximum height, cm, not less than height_min; people without a height pass
lat
query · number
Search centre latitude for scope=nearby; requires lon. Does not change the viewer's city.
lon
query · number
Search centre longitude for scope=nearby; requires lat
city
query · string
Place name for the same-city fallback when a profile has no coordinates
country
query · string
ISO 3166-1 alpha-2 with city
age_min
query · integer
Narrows the viewer's age range. Empty intersection with the profile returns an empty feed, not 422.
age_max
query · integer
Narrows the viewer's age range. Must be ≥ age_min when both are set.
intent
query · "serious" | "casual" | "friendship" | "arrangement"
One intent from the viewer's profile. Candidates must have it too.
language
query · string
ISO 639-1. Candidates must list it. Does not bypass the shared-language rule.
gender
query · "male" | "female"
Must be the opposite of the viewer.
cursor
query · string
Opaque next page, from the previous next_cursor. Invalid cursor is 422.

Ответы

200 Candidates
ПолеОписание
itemsобязательно
object[]
network_idобязательно
string (uuid)
partnerобязательно
object
id
string (uuid)
name
string
Name of the platform where the person registered.
display_nameобязательно
string
ageобязательно
integer
genderобязательно
"male" | "female"
cityобязательно
string
country
string
ISO 3166-1 alpha-2
distance_km
integer
only if this person turned on show_distance. Kilometres from the search centre (viewer's city, or lat/lon)
languagesобязательно
string[]
bioобязательно
string
In the viewer's main language when the viewer doesn't speak the original
bio_original
string
bio_translatedобязательно
boolean
Show a "translated" mark when true
photosобязательно
string (uri)[]
videos
object[]
but disabled in the production executable pending frame moderation. Absent when videos capability is false.
urlобязательно
string (uri)
Public MP4, at most 15 seconds and 20 MB
posterобязательно
string (uri)
Still frame. Shown before playback and by apps that do not play video. Moderated like a photo
photo_verifiedобязательно
boolean
The person's app verified they match the photos, and the network counts it
verified_by
"partner"
Who verified the photos: partner — the person's app, with a method approved by the network
intentsобязательно
string[]
arrangement_role
string
Shown only to viewers who have the arrangement intent themselves
height_cm
integer
Absent when the person didn't specify it
super_like
object
This person sent the viewer a super like (feed, incoming likes, like.received)
message
string
In the viewer's main language when they don't speak the original
message_original
string
translated
boolean
lang
string
flags
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )
remaining_todayобязательно
integer
How many new people this member can still be shown today (UTC). Repeats do not decrease it.
next_cursor
string
Pass as cursor for the next page. Absent on the last page.
404 Member, match or message not found (or not yours)
409 world_consent_required — scope=world for a member without consent.world; member_hidden — the member is hidden (consent.network: false)
422 Validation failed; message says which field

Лайки и мэтчи

Likes and dislikes across apps; a mutual like creates a match that opens chat.

Like a member of another app

POST/v1/likes

The liked member's platform gets like.received. If the like is mutual a match is created and both platforms get match.created. Liking a member of your own app through the network is not allowed (403).

  • You can only like someone your user could see: a block either way, the other environment (test vs live), or a hidden member who hasn't liked your user → 404, as if the profile didn't exist.
  • A repeated like changes nothing and sends no event; upgrading a like to a super like sends like.received again.
  • While your user's profile is in moderation, their likes are stored but the recipient gets like.received only after the profile passes; if it is rejected, shown likes are withdrawn (like.withdrawn, rejected).

Super like: super: true with an optional message (up to 200 characters, no contact details — 422). The recipient sees it right away, before any match: like.received has super: true and the sender's profile carries super_like with the message in the recipient's language; in the feed and in incoming likes such a person comes first. Show it clearly highlighted. When a match happens, the message becomes the first message of the chat (message.created to the recipient's platform). At most 5 super likes a day per member — then 429 super_like_limit; a regular like upgraded to a super like counts too. How many your users get (and whether they pay) is up to you.

curl -X POST 'https://api.pereval.dating/v1/likes' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "X-Action-Ref: $PEREVAL_ACTION_REF" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "super": false,
  "message": "string"
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/likes');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'X-Action-Ref: ' . getenv('PEREVAL_ACTION_REF'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'from' => 'string',
        'to' => '6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44',
        'super' => false,
        'message' => 'string',
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/likes",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}", "X-Action-Ref": os.environ['PEREVAL_ACTION_REF']},
    json={
        "from": "string",
        "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
        "super": False,
        "message": "string"
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/likes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "X-Action-Ref": process.env.PEREVAL_ACTION_REF,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "from": "string",
    "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "super": false,
    "message": "string"
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "super": false,
  "message": "string"
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/likes", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("X-Action-Ref", os.Getenv("PEREVAL_ACTION_REF"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

X-Action-Ref
header · string
Required for superlikes after commerce enrollment, including zero-price entitlements. Same reference only for the same action payload.

Тело запроса

ПолеОписание
fromобязательно
string
Your user's external_id
toобязательно
string (uuid)
network_id from the feed
super
boolean
default false
message
string
Super like only: shown before the match, becomes the first message after it · ≤ 200 chars

Ответы

200 Like stored
ПолеОписание
matchedобязательно
boolean
match_id
string (uuid)
Present when matched
403 Not allowed (e.g. liking or disliking a member of your own app, or a suspended member)
404 Member, match or message not found (or not yours)
409 member_hidden — a hidden member can only like back someone who liked them, without a super like
422 Validation failed; message says which field
429 super_like_limit — more than 5 super likes in 24 h

Take a like (or super like) back before it became a match

DELETE/v1/likes/{network_id}

The liked member's platform gets like.withdrawn. After a match — 409 matched; close the match instead.

curl -X DELETE 'https://api.pereval.dating/v1/likes/{network_id}?from=VALUE' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "X-Action-Ref: $PEREVAL_ACTION_REF"
<?php
$ch = curl_init('https://api.pereval.dating/v1/likes/{network_id}?from=VALUE');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'X-Action-Ref: ' . getenv('PEREVAL_ACTION_REF'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.delete(
    "https://api.pereval.dating/v1/likes/{network_id}?from=VALUE",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}", "X-Action-Ref": os.environ['PEREVAL_ACTION_REF']},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/likes/{network_id}?from=VALUE", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "X-Action-Ref": process.env.PEREVAL_ACTION_REF,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
req, _ := http.NewRequestWithContext(ctx, "DELETE", "https://api.pereval.dating/v1/likes/{network_id}?from=VALUE", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("X-Action-Ref", os.Getenv("PEREVAL_ACTION_REF"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

X-Action-Ref
header · string
Required after commerce enrollment. Replays of the same undo must use the same reference.
network_idобязательно
path · string (uuid)
fromобязательно
query · string
Your user's external_id

Ответы

204 Like removed
404 Member, match or message not found (or not yours)
409 matched — the like already became a match

Bring a skipped profile back

DELETE/v1/dislikes/{network_id}

Removes the dislike — the profile may appear in the feed again.

curl -X DELETE 'https://api.pereval.dating/v1/dislikes/{network_id}?from=VALUE' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "X-Action-Ref: $PEREVAL_ACTION_REF"
<?php
$ch = curl_init('https://api.pereval.dating/v1/dislikes/{network_id}?from=VALUE');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'X-Action-Ref: ' . getenv('PEREVAL_ACTION_REF'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.delete(
    "https://api.pereval.dating/v1/dislikes/{network_id}?from=VALUE",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}", "X-Action-Ref": os.environ['PEREVAL_ACTION_REF']},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/dislikes/{network_id}?from=VALUE", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "X-Action-Ref": process.env.PEREVAL_ACTION_REF,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
req, _ := http.NewRequestWithContext(ctx, "DELETE", "https://api.pereval.dating/v1/dislikes/{network_id}?from=VALUE", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("X-Action-Ref", os.Getenv("PEREVAL_ACTION_REF"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

X-Action-Ref
header · string
Required after commerce enrollment. Replays of the same undo must use the same reference.
network_idобязательно
path · string (uuid)
fromобязательно
query · string
Your user's external_id

Ответы

204 Dislike removed
404 Member, match or message not found (or not yours)

Your user saw a profile and didn't like it

POST/v1/dislikes

Send it when your user swiped left or scrolled past a profile without liking. Only your app knows what the user actually saw, so the network never treats a served feed as seen. The profile leaves the user's feed for 30 days. One pair or a batch of up to 100.

curl -X POST 'https://api.pereval.dating/v1/dislikes' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "erase": false
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/dislikes');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'from' => 'string',
        'to' => '6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44',
        'erase' => false,
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/dislikes",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "from": "string",
        "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
        "erase": False
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/dislikes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "from": "string",
    "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "erase": false
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "erase": false
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/dislikes", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
fromобязательно
string
Your user's external_id
toобязательно
string (uuid)
network_id from the feed
erase
boolean
blocks only: delete the match, messages and likes for both · default false

Ответы

204 Stored
403 Not allowed (e.g. liking or disliking a member of your own app, or a suspended member)
404 Member, match or message not found (or not yours)

Who liked this member and waits for an answer

GET/v1/members/{external_id}/likes

Without mutual likes, dislikes and blocks. Rebuilds "Liked you" when events were lost.

curl -X GET 'https://api.pereval.dating/v1/members/user-123/likes' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/likes');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/members/user-123/likes",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/likes", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/members/user-123/likes", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.
limit
query · integer

Ответы

200 Incoming likes, newest first
ПолеОписание
itemsобязательно
object[]
fromобязательно
object
A member of another app as seen by your user.
network_idобязательно
string (uuid)
partnerобязательно
object
id
string (uuid)
name
string
Name of the platform where the person registered.
display_nameобязательно
string
ageобязательно
integer
genderобязательно
"male" | "female"
cityобязательно
string
country
string
ISO 3166-1 alpha-2
distance_km
integer
only if this person turned on show_distance. Kilometres from the search centre (viewer's city, or lat/lon)
languagesобязательно
string[]
bioобязательно
string
In the viewer's main language when the viewer doesn't speak the original
bio_original
string
bio_translatedобязательно
boolean
Show a "translated" mark when true
photosобязательно
string (uri)[]
videos
object[]
but disabled in the production executable pending frame moderation. Absent when videos capability is false.
urlобязательно
string (uri)
Public MP4, at most 15 seconds and 20 MB
posterобязательно
string (uri)
Still frame. Shown before playback and by apps that do not play video. Moderated like a photo
photo_verifiedобязательно
boolean
The person's app verified they match the photos, and the network counts it
verified_by
"partner"
Who verified the photos: partner — the person's app, with a method approved by the network
intentsобязательно
string[]
arrangement_role
string
Shown only to viewers who have the arrangement intent themselves
height_cm
integer
Absent when the person didn't specify it
super_like
object
This person sent the viewer a super like (feed, incoming likes, like.received)
message
string
In the viewer's main language when they don't speak the original
message_original
string
translated
boolean
lang
string
flags
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )
liked_atобязательно
string (date-time)
404 Member, match or message not found (or not yours)

Open matches of a member

GET/v1/members/{external_id}/matches

curl -X GET 'https://api.pereval.dating/v1/members/user-123/matches' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/matches');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/members/user-123/matches",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/matches", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/members/user-123/matches", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string
Your user id.

Ответы

200 Matches, newest first
ПолеОписание
itemsобязательно
object[]
match_idобязательно
string (uuid)
created_atобязательно
string (date-time)
withобязательно
object
A member of another app as seen by your user.
network_idобязательно
string (uuid)
partnerобязательно
object
id
string (uuid)
name
string
Name of the platform where the person registered.
display_nameобязательно
string
ageобязательно
integer
genderобязательно
"male" | "female"
cityобязательно
string
country
string
ISO 3166-1 alpha-2
distance_km
integer
only if this person turned on show_distance. Kilometres from the search centre (viewer's city, or lat/lon)
languagesобязательно
string[]
bioобязательно
string
In the viewer's main language when the viewer doesn't speak the original
bio_original
string
bio_translatedобязательно
boolean
Show a "translated" mark when true
photosобязательно
string (uri)[]
videos
object[]
but disabled in the production executable pending frame moderation. Absent when videos capability is false.
urlобязательно
string (uri)
Public MP4, at most 15 seconds and 20 MB
posterобязательно
string (uri)
Still frame. Shown before playback and by apps that do not play video. Moderated like a photo
photo_verifiedобязательно
boolean
The person's app verified they match the photos, and the network counts it
verified_by
"partner"
Who verified the photos: partner — the person's app, with a method approved by the network
intentsобязательно
string[]
arrangement_role
string
Shown only to viewers who have the arrangement intent themselves
height_cm
integer
Absent when the person didn't specify it
super_like
object
This person sent the viewer a super like (feed, incoming likes, like.received)
message
string
In the viewer's main language when they don't speak the original
message_original
string
translated
boolean
lang
string
flags
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )
frozen
boolean
Omitted when false
freeze_reason
"account_deleting" | "suspended" | "partner_suspended"
Present when frozen

Close or erase a match (unmatch)

DELETE/v1/matches/{match_id}

Without erase the match is closed and both keep the history (match.closed, reason: unmatched). With erase=true the match, its messages and both likes are deleted for both — the other platform gets match.erased and should remove the chat; the two won't meet in each other's feeds for cooldown_days. If either of them reported the other, a hidden copy of the chat is kept for moderation for 90 days. To hide the history only for your user, use POST /v1/matches/{match_id}/clear.

curl -X DELETE 'https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10?by=user-123' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10?by=user-123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.delete(
    "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10?by=user-123",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10?by=user-123", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
req, _ := http.NewRequestWithContext(ctx, "DELETE", "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10?by=user-123", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

match_idобязательно
path · string (uuid)
byобязательно
query · string
external_id of your user who unmatches
erase
query · boolean
Delete the match, messages and likes for both
cooldown_days
query · integer
with erase. Days before they can see each other in the feed again; for "never" block instead

Ответы

204 Closed (match.closed) or erased (match.erased)
404 Member, match or message not found (or not yours)
422 Validation failed; message says which field

Сообщения

Text chat inside a match, machine-translated when the recipient doesn't speak the sender's language.

Delete the chat history for one side

POST/v1/matches/{match_id}/clear

"Delete chat" for your user only: the history before now is no longer returned to them; the other person keeps everything, the match stays, and new messages go both ways as usual.

curl -X POST 'https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/clear' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "as": "string"
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/clear');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'as' => 'string',
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/clear",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "as": "string"
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/clear", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "as": "string"
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "as": "string"
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/clear", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

match_idобязательно
path · string (uuid)

Тело запроса

ПолеОписание
asобязательно
string
external_id of your user

Ответы

204 Cleared
404 Member, match or message not found (or not yours)

Conversation history as seen by a member

GET/v1/matches/{match_id}/messages

Newest first by (created_at, id). Messages from the other side are in the viewer's language with the original and scam flags; own messages carry read_at and, when supplied, client_message_id for retry reconciliation. The before message must belong to this match. Closure preserves history; erasure removes it.

curl -X GET 'https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages?as=user-123' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages?as=user-123');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages?as=user-123",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages?as=user-123", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages?as=user-123", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

match_idобязательно
path · string (uuid)
asобязательно
query · string
external_id of the viewer
before
query · string (uuid)
Message id — returns older messages (pagination cursor)
limit
query · integer

Ответы

200 Messages
ПолеОписание
itemsобязательно
object[]
idобязательно
string (uuid)
fromобязательно
string
"me" for the viewer's own messages, otherwise the sender's network_id
client_message_id
string
Author history/POST echo when supplied; also included in message.created for reconciliation
textобязательно
string
text_original
string
translatedобязательно
boolean
langобязательно
string
created_atобязательно
string (date-time)
suspicious
boolean
Messages from the other side only
flags
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )
read_at
string (date-time)
Own messages only: when the other side read it
404 Member, match or message not found (or not yours)

Send a message

POST/v1/matches/{match_id}/messages

The network detects the language from the text; short replies ("ok", "haha", emoji) are attributed to one of the sender's languages, the main one first. If the recipient doesn't speak it, the message is translated in the background into the recipient's main language, and the recipient's platform gets message.created once the translation is ready. If translation fails the message is still delivered in the original language (translated: false).

Cheap scam signals are checked: links, phone numbers and invitations to other messengers early in a chat, talk about money, the same text sent to many people. The message is delivered anyway, with suspicious and flags, so the recipient's app can show a warning. Members in their first 7 days can start at most 20 conversations a day (429 first_messages_limit); replying in existing conversations is unlimited.

optional client_message_id is sender-scoped (not match-scoped), at most 128 UTF-8 bytes, without surrounding whitespace. Repeating the same ID, match and trimmed text returns the original message, including after match closure; changing match/text returns 409 idempotency_conflict. Ownership/auth are checked before replay. A suspended platform key is never authorized by a replay. After message retention or explicit match erasure, the original is no longer available: this is not a permanent idempotency reservation. Persist the returned message ID; recover a timed-out response through author history.

curl -X POST 'https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "string",
  "text": "string",
  "client_message_id": "string"
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'from' => 'string',
        'text' => 'string',
        'client_message_id' => 'string',
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "from": "string",
        "text": "string",
        "client_message_id": "string"
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "from": "string",
    "text": "string",
    "client_message_id": "string"
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "from": "string",
  "text": "string",
  "client_message_id": "string"
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/messages", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

match_idобязательно
path · string (uuid)

Тело запроса

ПолеОписание
fromобязательно
string
Sender's external_id
textобязательно
string
1–4000 chars
client_message_id
string
Optional sender-scoped retry ID; maximum 128 UTF-8 bytes, no surrounding whitespace · 1–128 chars

Ответы

200 Sent
ПолеОписание
idобязательно
string (uuid)
fromобязательно
string
"me" for the viewer's own messages, otherwise the sender's network_id
client_message_id
string
Author history/POST echo when supplied; also included in message.created for reconciliation
textобязательно
string
text_original
string
translatedобязательно
boolean
langобязательно
string
created_atобязательно
string (date-time)
suspicious
boolean
Messages from the other side only
flags
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )
read_at
string (date-time)
Own messages only: when the other side read it
404 Member, match or message not found (or not yours)
409 match_closed, match_frozen (including partner suspension), or idempotency_conflict
422 Validation failed; message says which field
429 first_messages_limit — a new member started too many conversations today, or rate_limited

Your user read the conversation

POST/v1/matches/{match_id}/read

Marks the other side's messages up to up_to (inclusive) as read; the other platform gets message.read.

curl -X POST 'https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/read' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "as": "string",
  "up_to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/read');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'as' => 'string',
        'up_to' => '6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44',
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/read",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "as": "string",
        "up_to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/read", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "as": "string",
    "up_to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "as": "string",
  "up_to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44"
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/matches/7d2a0f4e-1b6c-4e8a-9f3d-5c1b2a7e9d10/read", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

match_idобязательно
path · string (uuid)

Тело запроса

ПолеОписание
asобязательно
string
external_id of the reader
up_toобязательно
string (uuid)
Id of the last message read

Ответы

204 Marked
404 Member, match or message not found (or not yours)

Безопасность

Blocks and reports. Reports reach the reported member's platform; serious ones suspend the member network-wide.

Block a member

POST/v1/blocks

Both disappear from each other's feeds for good. Without erase an open match between them is closed (match.closed, reason: blocked), the history stays. With erase: true the match, messages and likes are deleted for both (match.erased, reason: blocked); if either reported the other, a hidden copy is kept for moderation for 90 days.

curl -X POST 'https://api.pereval.dating/v1/blocks' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "erase": false
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/blocks');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'from' => 'string',
        'to' => '6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44',
        'erase' => false,
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/blocks",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "from": "string",
        "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
        "erase": False
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/blocks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "from": "string",
    "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "erase": false
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "erase": false
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/blocks", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
fromобязательно
string
Your user's external_id
toобязательно
string (uuid)
network_id from the feed
erase
boolean
blocks only: delete the match, messages and likes for both · default false

Ответы

204 Blocked
404 Member, match or message not found (or not yours)

Report a member

POST/v1/reports

The reported member's platform gets member.reported. A report with reason: underage or commercial, or reports from 3 different people, suspends the member from the network until review (member.suspended): they are out of feeds and their matches are frozen (match.frozen with reason: suspended, 409 match_frozen for both). Their platform brings them back with POST /v1/members/{external_id}/reinstate.

curl -X POST 'https://api.pereval.dating/v1/reports' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "reason": "fake",
  "comment": "string"
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/reports');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'from' => 'string',
        'to' => '6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44',
        'reason' => 'fake',
        'comment' => 'string',
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/reports",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "from": "string",
        "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
        "reason": "fake",
        "comment": "string"
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/reports", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "from": "string",
    "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "reason": "fake",
    "comment": "string"
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "from": "string",
  "to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "reason": "fake",
  "comment": "string"
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/reports", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
fromобязательно
string
toобязательно
string (uuid)
reasonобязательно
"fake" | "scam" | "spam" | "offensive" | "underage" | "commercial" | "other"
commercial — offers sex for money; suspends the member at once
comment
string
≤ 1000 chars

Ответы

204 Reported
404 Member, match or message not found (or not yours)
422 Validation failed; message says which field

Языки

Languages the network translates profiles and messages into.

Translation languages

GET/v1/languages

ISO 639-1 codes the network translates between. Members may list other ISO 639-1 codes in languages, but a member whose main language is not in this list reads others in the original.

curl -X GET 'https://api.pereval.dating/v1/languages' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/languages');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/languages",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/languages", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/languages", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Ответы

200 Languages
ПолеОписание
translatedобязательно
string[]
ISO 639-1 codes
401 Missing, invalid or suspended API key

Photo check

The network's photo check for platforms without their own: the person takes it on a network page (consent, a requested gesture, a camera picture); the network compares the picture with each profile photo.

Start a photo check

POST/v1/photo-checks

Creates a check session with the person's profile photos (the network downloads them right away) and returns the url of the network's check page. Open it for the person — in the browser, a new tab or a webview with camera access; it can't be embedded in an iframe. The link is valid for 30 minutes and allows up to 5 attempts. On the page the person agrees to the comparison, shows the requested gesture and takes a picture with the camera; the network compares it with each photo. The result comes as photo_check.updated (after each attempt, a dispute and the moderator's decision) and in GET /v1/photo-checks/{id}; the page also posts {type: "pereval.photo_check", status} to the window that opened it.

The picture is never shown to you or other people and no facial features are stored: it is deleted right after a passed check, kept for 24 hours after a failed one (so the person can dispute the result) and until the moderator's decision if disputed, but never beyond 60 days in the active database from original check creation, including unresolved disputes. Personal metadata, references and source photos are also subject to that absolute limit. Updates, appeals and disputes do not restart it; expiry never approves a photo or removes a sanction. Operators must review before expiry. Public access ends at the earlier public token expiry independently of retained evidence. The consent names your platform and the network, which compares photos on your behalf.

curl -X POST 'https://api.pereval.dating/v1/photo-checks' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "ref": "u_123",
  "lang": "en",
  "return_url": "https://your.app/verified",
  "photos": [
    {
      "id": "ph1",
      "url": "https://cdn.your.app/ph1.jpg"
    },
    {
      "id": "ph2",
      "url": "https://cdn.your.app/ph2.jpg"
    }
  ]
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/photo-checks');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'ref' => 'u_123',
        'lang' => 'en',
        'return_url' => 'https://your.app/verified',
        'photos' => [[
            'id' => 'ph1',
            'url' => 'https://cdn.your.app/ph1.jpg',
        ], [
            'id' => 'ph2',
            'url' => 'https://cdn.your.app/ph2.jpg',
        ]],
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/photo-checks",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "ref": "u_123",
        "lang": "en",
        "return_url": "https://your.app/verified",
        "photos": [
            {
                "id": "ph1",
                "url": "https://cdn.your.app/ph1.jpg"
            },
            {
                "id": "ph2",
                "url": "https://cdn.your.app/ph2.jpg"
            }
        ]
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/photo-checks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "ref": "u_123",
    "lang": "en",
    "return_url": "https://your.app/verified",
    "photos": [
      {
        "id": "ph1",
        "url": "https://cdn.your.app/ph1.jpg"
      },
      {
        "id": "ph2",
        "url": "https://cdn.your.app/ph2.jpg"
      }
    ]
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "ref": "u_123",
  "lang": "en",
  "return_url": "https://your.app/verified",
  "photos": [
    {
      "id": "ph1",
      "url": "https://cdn.your.app/ph1.jpg"
    },
    {
      "id": "ph2",
      "url": "https://cdn.your.app/ph2.jpg"
    }
  ]
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/photo-checks", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
refобязательно
string
How you recognise the person in the result, usually their external_id · 1–200 chars
photosобязательно
object[]
1–9 items
id
string
Your photo id, unique in the session; default — the position (1, 2, …) · ≤ 100 chars
urlобязательно
string (uri)
Public HTTPS URL of a profile photo
lang
"ru" | "en"
Language of the check page · default "en"
return_url
string (uri)
Optional: the page's "Back" button · ≤ 500 chars

Ответы

201 Session created — open url for the person
ПолеОписание
idобязательно
string (uuid)
refобязательно
string
url
string (uri)
Only in the POST reply: the check page to open for the person
statusобязательно
"created" | "passed" | "partial" | "failed" | "disputed" | "resolved" | "expired"
created — not taken yet; passed — it's the person on every photo with a face; partial — some photos don't look like the picture; failed — see reason; disputed — the person disputed, a network moderator decides (usually within 24 hours); resolved — the moderator decided (note explains); expired — the link expired untaken.
reason
"no_match" | "no_face" | "many_faces" | "no_gesture" | "virtual_camera"
Why it failed
attemptsобязательно
integer
Pictures taken (up to 5 per session)
expires_atобязательно
string (date-time)
Until when the link allows attempts
checked_at
string (date-time)
The last picture
dispute_until
string (date-time)
Failed or partial: the person can dispute until then
disputed_at
string (date-time)
decided_at
string (date-time)
The moderator's decision
note
string
The moderator's explanation for the person
photosобязательно
object[]
idобязательно
string
urlобязательно
string (uri)
verdict
"match" | "no_match" | "unsure" | "no_face"
match — the same person; no_match — doesn't look like them; unsure — couldn't compare reliably; no_face — no face, not counted. Absent — not compared yet
created_atобязательно
string (date-time)
401 Missing, invalid or suspended API key
422 Validation failed; message says which field
429 photo_check_limit — more than 10 checks for this person (ref) a day; or rate_limited
503 photo_check_off — the comparison service is temporarily unavailable; retry later

Photo check result

GET/v1/photo-checks/{id}

curl -X GET 'https://api.pereval.dating/v1/photo-checks/{id}' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/photo-checks/{id}');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/photo-checks/{id}",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/photo-checks/{id}", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/photo-checks/{id}", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

idобязательно
path · string (uuid)

Ответы

200 The check (without url)
ПолеОписание
idобязательно
string (uuid)
refобязательно
string
url
string (uri)
Only in the POST reply: the check page to open for the person
statusобязательно
"created" | "passed" | "partial" | "failed" | "disputed" | "resolved" | "expired"
created — not taken yet; passed — it's the person on every photo with a face; partial — some photos don't look like the picture; failed — see reason; disputed — the person disputed, a network moderator decides (usually within 24 hours); resolved — the moderator decided (note explains); expired — the link expired untaken.
reason
"no_match" | "no_face" | "many_faces" | "no_gesture" | "virtual_camera"
Why it failed
attemptsобязательно
integer
Pictures taken (up to 5 per session)
expires_atобязательно
string (date-time)
Until when the link allows attempts
checked_at
string (date-time)
The last picture
dispute_until
string (date-time)
Failed or partial: the person can dispute until then
disputed_at
string (date-time)
decided_at
string (date-time)
The moderator's decision
note
string
The moderator's explanation for the person
photosобязательно
object[]
idобязательно
string
urlобязательно
string (uri)
verdict
"match" | "no_match" | "unsure" | "no_face"
match — the same person; no_match — doesn't look like them; unsure — couldn't compare reliably; no_face — no face, not counted. Absent — not compared yet
created_atобязательно
string (date-time)
401 Missing, invalid or suspended API key
404 Member, match or message not found (or not yours)

Забор событий

Pull events yourself — for platforms without a webhook URL and for recovery after an outage.

Pull events

GET/v1/events

Events of your environment in seq order after the given cursor; pulled events count as delivered. If your platform has no webhook URL, the network doesn't push — you pull. Events are kept for 7 days. The data of each event is the same as in the webhook of that type.

curl -X GET 'https://api.pereval.dating/v1/events' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/events');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/events",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/events", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/events", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

after
query · integer
next_after from the previous response (0 — from the oldest kept event)
limit
query · integer

Ответы

200 Events
ПолеОписание
itemsобязательно
object[]
seqобязательно
integer
idобязательно
string
typeобязательно
string
created_atобязательно
string (date-time)
dataобязательно
object
next_afterобязательно
integer
Pass as after next time

MCP

Model Context Protocol JSON-RPC 2.0 for AI agents. Same Bearer key as REST. POST /mcp — methods initialize, tools/list, tools/call, ping. Protocol version 2024-11-05.

Model Context Protocol (JSON-RPC)

POST/mcp

Same API key as the REST API. Methods: initialize, tools/list, tools/call, ping. Tools: upsert_member, get_feed, like, dislike, send_message, list_events, list_languages. A tools/call result is JSON in result.content[].text. RPC errors use JSON-RPC error; tool failures set isError: true and keep HTTP 200. Missing key — HTTP 401.

curl -X POST 'https://api.pereval.dating/mcp' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "jsonrpc": "2.0",
  "id": {},
  "method": "tools/list",
  "params": {}
}'
<?php
$ch = curl_init('https://api.pereval.dating/mcp');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'jsonrpc' => '2.0',
        'id' => [

        ],
        'method' => 'tools/list',
        'params' => [

        ],
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/mcp",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "jsonrpc": "2.0",
        "id": {},
        "method": "tools/list",
        "params": {}
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/mcp", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "jsonrpc": "2.0",
    "id": {},
    "method": "tools/list",
    "params": {}
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
body := []byte(`{
  "jsonrpc": "2.0",
  "id": {},
  "method": "tools/list",
  "params": {}
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/mcp", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
jsonrpcобязательно
"2.0"
id
object
number, string or null (null = notification, no response)
methodобязательно
string
params
object

Ответы

200 JSON-RPC result or error
401 Missing or invalid API key

Дополнительные услуги

Current own boost and remaining time; voluntary cancellation is not supported

GET/v1/members/{external_id}/boost

curl -X GET 'https://api.pereval.dating/v1/members/user-123/boost' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/boost');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/members/user-123/boost",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/boost", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/members/user-123/boost", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string

Ответы

200 Boost state
ПолеОписание
generationобязательно
string (uuid)
stateобязательно
"active" | "paused" | "completed"
started_atобязательно
string (date-time)
remaining_msобязательно
integer
≥ 0
api_placementsобязательно
integer
≥ 0
404 Own boost not found
503 Pilot unavailable

Declare receiving support independently of purchases

PUT/v1/commerce/delivery

curl -X PUT 'https://api.pereval.dating/v1/commerce/delivery' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "format": "swipe",
  "enabled": true
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/commerce/delivery');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'format' => 'swipe',
        'enabled' => true,
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.put(
    "https://api.pereval.dating/v1/commerce/delivery",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "format": "swipe",
        "enabled": True
    },
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/v1/commerce/delivery", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "format": "swipe",
    "enabled": true
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
body := []byte(`{
  "format": "swipe",
  "enabled": true
}`)

req, _ := http.NewRequestWithContext(ctx, "PUT", "https://api.pereval.dating/v1/commerce/delivery", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
formatобязательно
"swipe" | "search" | "none"
enabledобязательно
boolean

Ответы

200 Configured format version 1
422 Unsupported/inconsistent format; map receiving is not implemented
503 Pilot unavailable

Catalog in the enrolled account currency

GET/v1/commerce/catalog

curl -X GET 'https://api.pereval.dating/v1/commerce/catalog' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/commerce/catalog');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/commerce/catalog",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/commerce/catalog", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/commerce/catalog", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Ответы

200 Success
ПолеОписание
currencyобязательно
"TEST" | "RUB"
test_onlyобязательно
boolean
itemsобязательно
object[]
skuобязательно
string
capabilityобязательно
"superlike" | "rewind" | "incognito" | "boost"
unitобязательно
"action" | "minute" | "day"
unit_priceобязательно
integer (int64)
≥ 0
min_quantityобязательно
integer
≥ 1
max_quantityобязательно
integer
≥ 1
fulfillment_readyобязательно
boolean
versionобязательно
integer
≥ 1
currencyобязательно
"TEST" | "RUB"
401 See commerce-pilot.md for error codes
402 See commerce-pilot.md for error codes
403 See commerce-pilot.md for error codes
404 See commerce-pilot.md for error codes
409 See commerce-pilot.md for error codes
422 See commerce-pilot.md for error codes
429 See commerce-pilot.md for error codes
503 See commerce-pilot.md for error codes

TEST credits or zero-only RUB balance

GET/v1/commerce/balance

curl -X GET 'https://api.pereval.dating/v1/commerce/balance' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/commerce/balance');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/commerce/balance",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/commerce/balance", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/commerce/balance", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Ответы

200 Success
ПолеОписание
currencyобязательно
"TEST" | "RUB"
test_onlyобязательно
boolean
balanceобязательно
integer (int64)
≥ 0
401 See commerce-pilot.md for error codes
402 See commerce-pilot.md for error codes
403 See commerce-pilot.md for error codes
404 See commerce-pilot.md for error codes
409 See commerce-pilot.md for error codes
422 See commerce-pilot.md for error codes
429 See commerce-pilot.md for error codes
503 See commerce-pilot.md for error codes

Create a five-minute price snapshot; no debit

POST/v1/commerce/quotes

curl -X POST 'https://api.pereval.dating/v1/commerce/quotes' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "member": "string",
  "sku": "string",
  "quantity": 1
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/commerce/quotes');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'member' => 'string',
        'sku' => 'string',
        'quantity' => 1,
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/commerce/quotes",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "member": "string",
        "sku": "string",
        "quantity": 1
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/commerce/quotes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "member": "string",
    "sku": "string",
    "quantity": 1
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "member": "string",
  "sku": "string",
  "quantity": 1
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/commerce/quotes", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
memberобязательно
string
1–256 chars
skuобязательно
string
1–80 chars
quantityобязательно
integer
1–1000000

Ответы

200 Success
ПолеОписание
idобязательно
string (uuid)
member_idобязательно
string (uuid)
skuобязательно
string
capabilityобязательно
string
unitобязательно
string
quantityобязательно
integer
≥ 1
unit_priceобязательно
integer (int64)
≥ 0
totalобязательно
integer (int64)
≥ 0
catalog_versionобязательно
integer
expires_atобязательно
string (date-time)
currencyобязательно
"TEST" | "RUB"
401 See commerce-pilot.md for error codes
402 See commerce-pilot.md for error codes
403 See commerce-pilot.md for error codes
404 See commerce-pilot.md for error codes
409 See commerce-pilot.md for error codes
422 See commerce-pilot.md for error codes
429 See commerce-pilot.md for error codes
503 See commerce-pilot.md for error codes

Atomically purchase entitlement; durable replay by external_order_ref

POST/v1/commerce/orders

curl -X POST 'https://api.pereval.dating/v1/commerce/orders' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "X-Commerce-Key: $PEREVAL_COMMERCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "quote_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "external_order_ref": "string"
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/commerce/orders');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'X-Commerce-Key: ' . getenv('PEREVAL_COMMERCE_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'quote_id' => '6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44',
        'external_order_ref' => 'string',
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.post(
    "https://api.pereval.dating/v1/commerce/orders",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}", "X-Commerce-Key": os.environ['PEREVAL_COMMERCE_KEY']},
    json={
        "quote_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
        "external_order_ref": "string"
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/commerce/orders", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "X-Commerce-Key": process.env.PEREVAL_COMMERCE_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "quote_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "external_order_ref": "string"
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "quote_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "external_order_ref": "string"
}`)

req, _ := http.NewRequestWithContext(ctx, "POST", "https://api.pereval.dating/v1/commerce/orders", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("X-Commerce-Key", os.Getenv("PEREVAL_COMMERCE_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Тело запроса

ПолеОписание
quote_idобязательно
string (uuid)
external_order_refобязательно
string
1–128 chars

Ответы

200 Success
ПолеОписание
idобязательно
string (uuid)
external_order_refобязательно
string
quote_idобязательно
string (uuid)
totalобязательно
integer (int64)
≥ 0
created_atобязательно
string (date-time)
currencyобязательно
"TEST" | "RUB"
grant_idобязательно
string (uuid)
valid_fromобязательно
string (date-time)
valid_untilобязательно
string,null (date-time)
401 See commerce-pilot.md for error codes
402 See commerce-pilot.md for error codes
403 See commerce-pilot.md for error codes
404 See commerce-pilot.md for error codes
409 See commerce-pilot.md for error codes
422 See commerce-pilot.md for error codes
429 See commerce-pilot.md for error codes
503 See commerce-pilot.md for error codes

List own subject entitlements

GET/v1/members/{external_id}/entitlements

curl -X GET 'https://api.pereval.dating/v1/members/user-123/entitlements' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/entitlements');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/members/user-123/entitlements",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/entitlements", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/members/user-123/entitlements", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string

Ответы

200 Success
ПолеОписание
itemsобязательно
object[]
idобязательно
string (uuid)
capabilityобязательно
string
quantityобязательно
integer
≥ 1
usedобязательно
integer (int64)
≥ 0
valid_fromобязательно
string (date-time)
valid_untilобязательно
string,null (date-time)
revoked_atобязательно
string,null (date-time)
401 See commerce-pilot.md for error codes
402 See commerce-pilot.md for error codes
403 See commerce-pilot.md for error codes
404 See commerce-pilot.md for error codes
409 See commerce-pilot.md for error codes
422 See commerce-pilot.md for error codes
429 See commerce-pilot.md for error codes
503 See commerce-pilot.md for error codes

Explicitly enable incognito with a valid right or freely disable it

PUT/v1/members/{external_id}/incognito

curl -X PUT 'https://api.pereval.dating/v1/members/user-123/incognito' \
  -H "Authorization: Bearer $PEREVAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true
}'
<?php
$ch = curl_init('https://api.pereval.dating/v1/members/user-123/incognito');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'enabled' => true,
    ], JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.put(
    "https://api.pereval.dating/v1/members/user-123/incognito",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    json={
        "enabled": True
    },
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/members/user-123/incognito", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "enabled": true
  }),
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
body := []byte(`{
  "enabled": true
}`)

req, _ := http.NewRequestWithContext(ctx, "PUT", "https://api.pereval.dating/v1/members/user-123/incognito", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Параметры

external_idобязательно
path · string

Тело запроса

ПолеОписание
enabledобязательно
boolean

Ответы

200 Success
ПолеОписание
enabledобязательно
boolean
401 See commerce-pilot.md for error codes
402 See commerce-pilot.md for error codes
403 See commerce-pilot.md for error codes
404 See commerce-pilot.md for error codes
409 See commerce-pilot.md for error codes
422 See commerce-pilot.md for error codes
429 See commerce-pilot.md for error codes
503 See commerce-pilot.md for error codes

Прочее

Process liveness

GET/healthz

curl -X GET 'https://api.pereval.dating/healthz' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/healthz');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/healthz",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/healthz", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/healthz", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Ответы

200 Process is running

Database readiness, not a check of external moderation or AI availability

GET/readyz

curl -X GET 'https://api.pereval.dating/readyz' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/readyz');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/readyz",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
const res = await fetch("https://api.pereval.dating/readyz", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/readyz", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Ответы

200 Database responds within two seconds
503 Database unavailable (not_ready)

Runtime protocol and feature configuration

GET/v1/capabilities

curl -X GET 'https://api.pereval.dating/v1/capabilities' \
  -H "Authorization: Bearer $PEREVAL_API_KEY"
<?php
$ch = curl_init('https://api.pereval.dating/v1/capabilities');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('PEREVAL_API_KEY'),
    ],
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($status >= 400) {
    throw new RuntimeException("Pereval $status: $response");
}
$data = json_decode($response, true);
import os, requests

resp = requests.get(
    "https://api.pereval.dating/v1/capabilities",
    headers={"Authorization": f"Bearer {os.environ['PEREVAL_API_KEY']}"},
    timeout=20,
)
resp.raise_for_status()
data = resp.json()
const res = await fetch("https://api.pereval.dating/v1/capabilities", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.PEREVAL_API_KEY}`,
  },
});
if (!res.ok) throw new Error(`Pereval ${res.status}: ${await res.text()}`);
const data = await res.json();
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.pereval.dating/v1/capabilities", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("PEREVAL_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
	b, _ := io.ReadAll(resp.Body)

	return fmt.Errorf("pereval %d: %s", resp.StatusCode, b)
}

Ответы

200 Configuration, not proof that external dependencies are healthy
ПолеОписание
protocol_versionобязательно
"1.0.0"
productionобязательно
boolean
commerce_enabledобязательно
boolean
Full network commerce is enabled; does not imply this partner is enrolled
commerce_live_zero_onlyобязательно
boolean
Live checkout accepts zero RUB only; no monetary payment processor
commerce_pilotобязательно
boolean
TEST-only development pilot; false in production
client_message_idобязательно
true
videosобязательно
boolean
False in the production executable
photo_checksобязательно
boolean
Facecheck URL is configured
profile_moderationобязательно
boolean
Profile moderation is configured
401 Missing

События (вебхуки)

Every webhook is a POST with this envelope. Headers: X-Network-Signature: sha256=<hex> — HMAC-SHA256 of the raw body with your signing secret (verify it, constant-time compare); X-Network-Event: <type>. Reply 2xx quickly. Non-2xx or timeout (10 s) → retries after 10 s, 1 min, 5 min, 30 min, 2 h, 6 h, then daily for up to 3 days. Deliveries may repeat — dedupe by id. Order holds unless a delivery is retried. Without a webhook URL, pull the same events with GET /v1/events.

Проверка подписи и приём — в разделе «Вебхуки»

boost.started

Boost started

Durable boost lifecycle event, not a human impression. Reconcile current state with the member boost endpoint.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "boost.started",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "generation": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "remaining_ms": 0
  }
}

data

ПолеОписание
memberобязательно
string
Your member external_id
generationобязательно
string (uuid)
remaining_msобязательно
integer
≥ 0

boost.extended

Boost extended

Durable boost lifecycle event, not a human impression. Reconcile current state with the member boost endpoint.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "boost.extended",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "generation": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "remaining_ms": 0
  }
}

data

ПолеОписание
memberобязательно
string
Your member external_id
generationобязательно
string (uuid)
remaining_msобязательно
integer
≥ 0

boost.paused

Boost paused

Durable boost lifecycle event, not a human impression. Reconcile current state with the member boost endpoint.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "boost.paused",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "generation": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "remaining_ms": 0
  }
}

data

ПолеОписание
memberобязательно
string
Your member external_id
generationобязательно
string (uuid)
remaining_msобязательно
integer
≥ 0

boost.resumed

Boost resumed

Durable boost lifecycle event, not a human impression. Reconcile current state with the member boost endpoint.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "boost.resumed",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "generation": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "remaining_ms": 0
  }
}

data

ПолеОписание
memberобязательно
string
Your member external_id
generationобязательно
string (uuid)
remaining_msобязательно
integer
≥ 0

boost.completed

Boost completed

Durable boost lifecycle event, not a human impression. Reconcile current state with the member boost endpoint.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "boost.completed",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "generation": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "remaining_ms": 0
  }
}

data

ПолеОписание
memberобязательно
string
Your member external_id
generationобязательно
string (uuid)
remaining_msобязательно
integer
≥ 0

like.received

Someone from another app liked your user

Show it as an incoming like. Liking back via POST /v1/likes creates a match.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "like.received",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "to": "string",
    "from": {
      "network_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
      "partner": {
        "id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
        "name": "string"
      },
      "display_name": "string",
      "age": 1,
      "gender": "male",
      "city": "string",
      "country": "string",
      "distance_km": 1,
      "languages": [
        "string"
      ],
      "bio": "string",
      "bio_original": "string",
      "bio_translated": true,
      "photos": [
        "https://example.com/photo.jpg"
      ],
      "videos": [
        {
          "url": "https://example.com/photo.jpg",
          "poster": "https://example.com/photo.jpg"
        }
      ],
      "photo_verified": true,
      "verified_by": "partner",
      "intents": [
        "string"
      ],
      "arrangement_role": "string",
      "height_cm": 1,
      "super_like": {
        "message": "string",
        "message_original": "string",
        "translated": true,
        "lang": "string",
        "flags": [
          "link"
        ]
      }
    },
    "super": true
  }
}

data

ПолеОписание
toобязательно
string
Your user's external_id
fromобязательно
object
A member of another app as seen by your user.
network_idобязательно
string (uuid)
partnerобязательно
object
id
string (uuid)
name
string
Name of the platform where the person registered.
display_nameобязательно
string
ageобязательно
integer
genderобязательно
"male" | "female"
cityобязательно
string
country
string
ISO 3166-1 alpha-2
distance_km
integer
only if this person turned on show_distance. Kilometres from the search centre (viewer's city, or lat/lon)
languagesобязательно
string[]
bioобязательно
string
In the viewer's main language when the viewer doesn't speak the original
bio_original
string
bio_translatedобязательно
boolean
Show a "translated" mark when true
photosобязательно
string (uri)[]
videos
object[]
but disabled in the production executable pending frame moderation. Absent when videos capability is false.
urlобязательно
string (uri)
Public MP4, at most 15 seconds and 20 MB
posterобязательно
string (uri)
Still frame. Shown before playback and by apps that do not play video. Moderated like a photo
photo_verifiedобязательно
boolean
The person's app verified they match the photos, and the network counts it
verified_by
"partner"
Who verified the photos: partner — the person's app, with a method approved by the network
intentsобязательно
string[]
arrangement_role
string
Shown only to viewers who have the arrangement intent themselves
height_cm
integer
Absent when the person didn't specify it
super_like
object
This person sent the viewer a super like (feed, incoming likes, like.received)
message
string
In the viewer's main language when they don't speak the original
message_original
string
translated
boolean
lang
string
flags
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )
super
boolean
A super like: from.super_like has the message

like.withdrawn

A like your user hasn't answered is gone

Remove it from your user's incoming likes. it also comes when the person who liked is no longer available — deleting their account, hidden after reports, their profile rejected by moderation, or removed from the network (reason). If they come back (restored, reinstated, profile fixed), your user gets like.received again.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "like.withdrawn",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "to": "string",
    "from": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "reason": "unliked"
  }
}

data

ПолеОписание
toобязательно
string
Your user's external_id
fromобязательно
string (uuid)
network_id of the person who took the like back
reason
"unliked" | "account_deleting" | "suspended" | "rejected" | "account_deleted" | "left_network"
unliked — they took the like back themselves

match.created

A match was created

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "match.created",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "match_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "member": "string",
    "with": {
      "network_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
      "partner": {
        "id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
        "name": "string"
      },
      "display_name": "string",
      "age": 1,
      "gender": "male",
      "city": "string",
      "country": "string",
      "distance_km": 1,
      "languages": [
        "string"
      ],
      "bio": "string",
      "bio_original": "string",
      "bio_translated": true,
      "photos": [
        "https://example.com/photo.jpg"
      ],
      "videos": [
        {
          "url": "https://example.com/photo.jpg",
          "poster": "https://example.com/photo.jpg"
        }
      ],
      "photo_verified": true,
      "verified_by": "partner",
      "intents": [
        "string"
      ],
      "arrangement_role": "string",
      "height_cm": 1,
      "super_like": {
        "message": "string",
        "message_original": "string",
        "translated": true,
        "lang": "string",
        "flags": [
          "link"
        ]
      }
    }
  }
}

data

ПолеОписание
match_idобязательно
string (uuid)
memberобязательно
string
Your user's external_id
withобязательно
object
A member of another app as seen by your user.
network_idобязательно
string (uuid)
partnerобязательно
object
id
string (uuid)
name
string
Name of the platform where the person registered.
display_nameобязательно
string
ageобязательно
integer
genderобязательно
"male" | "female"
cityобязательно
string
country
string
ISO 3166-1 alpha-2
distance_km
integer
only if this person turned on show_distance. Kilometres from the search centre (viewer's city, or lat/lon)
languagesобязательно
string[]
bioобязательно
string
In the viewer's main language when the viewer doesn't speak the original
bio_original
string
bio_translatedобязательно
boolean
Show a "translated" mark when true
photosобязательно
string (uri)[]
videos
object[]
but disabled in the production executable pending frame moderation. Absent when videos capability is false.
urlобязательно
string (uri)
Public MP4, at most 15 seconds and 20 MB
posterобязательно
string (uri)
Still frame. Shown before playback and by apps that do not play video. Moderated like a photo
photo_verifiedобязательно
boolean
The person's app verified they match the photos, and the network counts it
verified_by
"partner"
Who verified the photos: partner — the person's app, with a method approved by the network
intentsобязательно
string[]
arrangement_role
string
Shown only to viewers who have the arrangement intent themselves
height_cm
integer
Absent when the person didn't specify it
super_like
object
This person sent the viewer a super like (feed, incoming likes, like.received)
message
string
In the viewer's main language when they don't speak the original
message_original
string
translated
boolean
lang
string
flags
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )

message.created

A new message for your user

Show a warning when suspicious is true — flags say why.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "message.created",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "match_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "to": "string",
    "message": {
      "id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
      "from": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
      "client_message_id": "string",
      "text": "string",
      "text_original": "string",
      "translated": true,
      "lang": "string",
      "created_at": "2026-10-24T12:00:00Z",
      "suspicious": true,
      "flags": [
        "link"
      ]
    }
  }
}

data

ПолеОписание
match_idобязательно
string (uuid)
toобязательно
string
Your user's external_id
messageобязательно
object
idобязательно
string (uuid)
Use it as up_to in markRead
fromобязательно
string (uuid)
Sender's network_id
client_message_id
string
Sender-scoped retry ID when supplied; deduplicate incoming messages by message.id
textобязательно
string
In your user's language when translated
text_original
string
translatedобязательно
boolean
langобязательно
string
Language of text
created_atобязательно
string (date-time)
suspiciousобязательно
boolean
flagsобязательно
"link" | "phone" | "messenger" | "money" | "mass_text"[]
Scam signals: link, phone, messenger (early in a chat), money, mass_text (the same text in several matches, only together with another signal )

message.read

The other side read your user's messages

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "message.read",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "match_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "member": "string",
    "by": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "up_to": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "read_at": "2026-10-24T12:00:00Z"
  }
}

data

ПолеОписание
match_idобязательно
string (uuid)
memberобязательно
string
Your user's external_id (the author)
byобязательно
string (uuid)
network_id of the reader
up_toобязательно
string (uuid)
Messages up to this one (inclusive) are read
read_atобязательно
string (date-time)

match.closed

A match of your user was closed by the other side

Make the chat read-only; the history stays. With peer_deleted: true (the other person deleted their account or their network data) also replace their name and photo with "Account deleted" / "Left the network".

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "match.closed",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "match_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "member": "string",
    "reason": "unmatched",
    "peer_deleted": true
  }
}

data

ПолеОписание
match_idобязательно
string (uuid)
memberобязательно
string
reasonобязательно
"unmatched" | "blocked" | "left_network" | "account_deleted"
peer_deleted
boolean
The other person is gone from the network: their profile is erased

match.erased

A match of your user was erased by the other side

The other side deleted the pair (or blocked with erase) — remove the chat and its history on your side.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "match.erased",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "match_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "member": "string",
    "reason": "unmatched"
  }
}

data

ПолеОписание
match_idобязательно
string (uuid)
memberобязательно
string
Your user's external_id
reasonобязательно
"unmatched" | "blocked"

match.frozen

The other person is deleting their account or was hidden after reports

The chat is read-only (409 match_frozen). reason: account_deleting — show "Account deleted"; it may come back with match.unfrozen (restored within 30 days) or end with match.closed + peer_deleted. suspended — the network hid them after reports; show "Account unavailable" until match.unfrozen (their platform reinstated them) or match.closed. partner_suspended means either platform is suspended. Restore messaging only after an unfreeze or a fresh bootstrap confirming frozen: false; another independent freeze may remain.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "match.frozen",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "match_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "member": "string",
    "reason": "account_deleting"
  }
}

data

ПолеОписание
match_idобязательно
string (uuid)
memberобязательно
string
Your user's external_id
reason
"account_deleting" | "suspended" | "partner_suspended"
partner_suspended

match.unfrozen

The other person restored their account or was reinstated after reports

The chat is back as it was — their name, photo and messaging.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "match.unfrozen",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "match_id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "member": "string"
  }
}

data

ПолеОписание
match_idобязательно
string (uuid)
memberобязательно
string
Your user's external_id

member.reported

Your user was reported by someone from another app

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "member.reported",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "reason": "string",
    "comment": "string"
  }
}

data

ПолеОписание
member
string
reason
string
comment
string

member.suspended

Your user was hidden from the network pending review

After a report with underage or commercial, or reports from 3 different people. Their matches are frozen until you review the reports: block the user in your app or bring them back with POST /v1/members/{external_id}/reinstate.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "member.suspended",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "reason": "string"
  }
}

data

ПолеОписание
member
string
reason
string

member.rejected

Your user's profile did not pass the network's moderation

The profile is hidden from feeds and cannot like until you send a corrected one.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "member.rejected",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "member": "string",
    "reason": "photo_nudity",
    "photo": "https://example.com/photo.jpg",
    "video": "https://example.com/photo.jpg"
  }
}

data

ПолеОписание
memberобязательно
string
reasonобязательно
"photo_nudity" | "offensive_text" | "contact_info" | "photo_unavailable" | "video_invalid" | "video_too_long" | "video_unavailable"
photo
string (uri)
The photo or poster that did not pass (photo_* reasons)
video
string (uri)
The clip that did not pass (video_* reasons)

photo_check.updated

A new result of a photo check

After each attempt, when the person disputes the result and when the network moderator decides — go by the latest. Passed (passed or resolved with the photos matched) — you may claim verification: {provider: "pereval", check_id}.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "photo_check.updated",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "id": "6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
    "ref": "string",
    "url": "https://example.com/photo.jpg",
    "status": "created",
    "reason": "no_match",
    "attempts": 1,
    "expires_at": "2026-10-24T12:00:00Z",
    "checked_at": "2026-10-24T12:00:00Z",
    "dispute_until": "2026-10-24T12:00:00Z",
    "disputed_at": "2026-10-24T12:00:00Z",
    "decided_at": "2026-10-24T12:00:00Z",
    "note": "string",
    "photos": [
      {
        "id": "string",
        "url": "https://example.com/photo.jpg",
        "verdict": "match"
      }
    ],
    "created_at": "2026-10-24T12:00:00Z"
  }
}

data

ПолеОписание
idобязательно
string (uuid)
refобязательно
string
url
string (uri)
Only in the POST reply: the check page to open for the person
statusобязательно
"created" | "passed" | "partial" | "failed" | "disputed" | "resolved" | "expired"
created — not taken yet; passed — it's the person on every photo with a face; partial — some photos don't look like the picture; failed — see reason; disputed — the person disputed, a network moderator decides (usually within 24 hours); resolved — the moderator decided (note explains); expired — the link expired untaken.
reason
"no_match" | "no_face" | "many_faces" | "no_gesture" | "virtual_camera"
Why it failed
attemptsобязательно
integer
Pictures taken (up to 5 per session)
expires_atобязательно
string (date-time)
Until when the link allows attempts
checked_at
string (date-time)
The last picture
dispute_until
string (date-time)
Failed or partial: the person can dispute until then
disputed_at
string (date-time)
decided_at
string (date-time)
The moderator's decision
note
string
The moderator's explanation for the person
photosобязательно
object[]
idобязательно
string
urlобязательно
string (uri)
verdict
"match" | "no_match" | "unsure" | "no_face"
match — the same person; no_match — doesn't look like them; unsure — couldn't compare reliably; no_face — no face, not counted. Absent — not compared yet
created_atобязательно
string (date-time)

ping

Test event from the dashboard

Sent when you click "Send a test event" in the dashboard. Reply 2xx — this checks the URL and the signature.

{
  "id": "evt_6f1c2b4e-8a3d-4c55-9b1e-2f0a7d9c1e44",
  "type": "ping",
  "created_at": "2026-10-24T12:00:00Z",
  "data": {
    "message": "string"
  }
}

data

ПолеОписание
message
string

Непонятно? Напишите разработчику

Ответ и продолжение переписки — на сайте по секретной ссылке, которую вы получите после отправки. Входящая почта не принимается.