Kostenlos, ohne Konto, offline prüfbar: Ihre Nutzer weisen nach, dass sie ein Qevik-Konto haben, und Sie prüfen die Signatur auf Ihrem Server.
Sie verkaufen Artikel online? Zur Händlerschnittstelle
Domain anmelden
Sie melden Ihre Domain an und veröffentlichen eine Prüfdatei. Ein Konto ist nicht nötig.
Der Nutzer weist sein Konto nach
Von Ihrer Website aus erlaubt der Nutzer auf qevik.com die Übermittlung einer signierten Bestätigung.
Sie prüfen die Signatur
Ihr Server prüft die Bestätigung mit den öffentlichen Schlüsseln von Qevik, ohne Abruf bei jeder Anzeige.
Wählen Sie Text, Sprache und Hintergrund. Die Dateien werden unverändert verwendet, ohne Übersetzung oder Bearbeitung (Bedingungen, Ziffer 5).
Vorschau, wie bei einer Anzeige
Das Abzeichen steht neben dem Pseudonym (Bedingungen, Ziffer 5 b).
HTML-Ausschnitt
<a href="https://qevik.com/verify/de#ATTESTATION" target="_blank" rel="noopener noreferrer" referrerpolicy="no-referrer">
<img src="/badges/qevik-badge-membre-de-clair.svg" alt="Qevik-Mitglied" width="105" height="28"></a>Die Plattform zeigt ein Qevik-Abzeichen nur
Die Plattform verbindet das Abzeichen mit keiner Aussage, die nahelegt, dass Qevik die Identität des Nutzers prüft, für ihn oder seine Leistungen einsteht, Gegenstände versichert, die Plattform empfiehlt oder ihr Partner ist.
Ihre Plattform öffnet /authorize auf qevik.com.
Der Nutzer meldet sich bei Qevik an und liest die Zustimmungsseite.
Stimmt er zu, leitet qevik.com ihn mit code, state und iss an Ihre Rücksprungadresse zurück.
Ihr Server prüft state und iss und tauscht den code über /token ein, von Server zu Server, zusammen mit dem öffentlichen Pseudonym des Nutzers (platform_handle).
Sie erhalten die signierte Bestätigung und prüfen sie auf Ihrem Server.
Sie fragen den Nutzer, ob das Abzeichen bei seinen Anzeigen erscheinen soll.
Die Wahl liegt beim Nutzer
Die Bedingungen verlangen es (Ziffer 5 a): Das Abzeichen erscheint nur, wenn der Nutzer es ausdrücklich gewählt hat, und verschwindet, sobald er die Wahl zurücknimmt.
Vor dem Ablauf erneuern Sie die Bestätigung und übermitteln dabei erneut das öffentliche Pseudonym des Nutzers; hat der Nutzer den Zugang widerrufen, wird die Erneuerung abgelehnt.
Ein Qevik-QR-Code wird ausschließlich auf qevik.com heruntergeladen. Ihre Schaltfläche „Herunterladen“ öffnet die Seite auf qevik.com in einem neuen Tab oder Fenster, mit rel="noopener noreferrer". Ihre Plattform erhält den QR-Code nie, kann ihn nicht über die API abrufen und bettet diese Seite nicht in einen Rahmen (iframe) ein.
GET /api/v1/public/oauth/authorize
GET https://qevik.com/api/v1/public/oauth/authorize
?client_id=plt_YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fyour-site.example%2Fqevik%2Fback # exact registered return URL
&response_type=code
&scope=attest%3Aaccount%20attest%3Atagged # or attest:account alone
&state=RANDOM_BOUND_TO_THE_USER_SESSION # mandatory, <= 512 chars
&code_challenge=BASE64URL_SHA256_OF_CODE_VERIFIER
&code_challenge_method=S256 # S256 only
&ui_locales=fr # optional: fr, de or en
# No platform_handle here: it goes to POST /token only.
# Refusals are never redirects: the user sees a Qevik page.redirect_uri ?state&iss&code
# Approved
https://your-site.example/qevik/back?state=...&iss=https://qevik.com&code=... # code: single use, 60 s
# Refused by the user
https://your-site.example/qevik/back?state=...&iss=https://qevik.com&error=access_denied
# Check state against the user's session and iss against https://qevik.com before anything else.POST /api/v1/public/oauth/token (authorization_code)
POST https://qevik.com/api/v1/public/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=CODE_FROM_THE_CALLBACK
&code_verifier=ORIGINAL_CODE_VERIFIER
&client_id=plt_YOUR_CLIENT_ID
&redirect_uri=https%3A%2F%2Fyour-site.example%2Fqevik%2Fback
&platform_handle=jean_d # the user's public name on your site, on every call
# Server to server only: browsers are refused by CORS. No client secret (public client, PKCE).POST /api/v1/public/oauth/token (refresh_token)
POST https://qevik.com/api/v1/public/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=LAST_REFRESH_TOKEN_RECEIVED
&client_id=plt_YOUR_CLIENT_ID
&platform_handle=jean_d # sent again: Qevik never stores it
# Refresh tokens rotate: keep the new one, the one you sent is dead.
# Sending a used refresh token (or a code twice) revokes the link: the user must consent again.200 /token
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"attestation": "eyJhbGciOiJFUzI1NiIsImtpZCI6Ii4uLiIsInR5cCI6InFldmlrLWF0dGVzdGF0aW9uK2p3dCJ9...",
"token_type": "qevik-attestation",
"expires_in": 2592000,
"refresh_token": "...",
"scope": "attest:account attest:tagged"
}OAuth errors /token
HTTP/1.1 400 Bad Request
{ "error": "invalid_request", "error_description": "platform_handle is invalid (PLT_014)." }
400 invalid_request missing or malformed parameter, invalid platform_handle
400 invalid_grant code or refresh token unknown, expired, used or revoked; PKCE mismatch
400 access_denied platform closed, or the user can no longer be attested (link revoked)
400 unsupported_grant_type anything but authorization_code or refresh_token
400 temporarily_unavailable the offer is closed
429 temporarily_unavailable 5 000 attestations per platform per UTC day; honour Retry-After
# platform_handle: NFC, 1 to 64 characters, letters and digits of any script, . _ - and single inner spaces.Attestation claims
header { "alg": "ES256", "kid": "...", "typ": "qevik-attestation+jwt" }
payload {
"iss": "https://qevik.com",
"aud": "plt_YOUR_CLIENT_ID", // a single string
"sub": "...", // pairwise: differs on every platform
"iat": 1761120000, "nbf": 1761120000, "exp": 1763712000, // 30 days
"jti": "...",
"att": "tagged", // "account" or "tagged"
"member": true, // always true
"tagged": true, // present only when att = "tagged"
"since": 2025, // account creation year
"dom": "your-site.example",
"platformHandle": "jean_d" // must equal the listing owner's name
}Die Bestätigung ist ein von Qevik signiertes JWT. Prüfen Sie es mit den öffentlichen Schlüsseln von Qevik und einer bewährten Bibliothek.
Bibliothek: jose, ab Version 5.2.
import { createRemoteJWKSet, jwtVerify } from "jose";
const ISSUER = "https://qevik.com";
const AUDIENCE = "plt_YOUR_CLIENT_ID";
// JWKS cached for 1 h; an unknown kid reloads it at most once every 5 minutes.
const JWKS = createRemoteJWKSet(new URL("https://qevik.com/.well-known/qevik-platform-jwks.json"), {
cacheMaxAge: 3_600_000,
cooldownDuration: 300_000,
});
/** Returns the claims, or null: a null hides the badge, it never shows an error. */
export async function verifyQevikAttestation(token, expectedHandle) {
if (typeof token !== "string" || token.length > 2048 || token.split(".").length !== 3) return null;
try {
const { payload } = await jwtVerify(token, JWKS, {
algorithms: ["ES256"],
issuer: ISSUER,
audience: AUDIENCE,
typ: "qevik-attestation+jwt",
clockTolerance: 60,
requiredClaims: ["exp", "nbf", "iat", "sub", "att", "member", "platformHandle"],
});
if (payload.member !== true) return null;
if (payload.att !== "account" && payload.att !== "tagged") return null;
if (payload.platformHandle !== expectedHandle) return null;
return payload; // "Qevik label" badge only if payload.tagged === true
} catch {
return null;
}
}
Prüfen Sie auf Ihrem Server, niemals im Browser des Nutzers.
Vor dem Livegang muss jeder Fall mit Ihrer Implementierung das erwartete Ergebnis liefern.
Diese Vektoren sind mit einem Testschlüssel signiert, der nur zu diesem Zweck veröffentlicht ist. Akzeptieren Sie ihn niemals im Produktivbetrieb.
| Fall | Erwartetes Ergebnis |
|---|---|
Gültige Bestätigung valid | Annehmen |
Abgelaufene Bestätigung expired | Ablehnen |
Veränderte Signatur badSignature | Ablehnen |
Audience einer anderen Plattform (aud) wrongAud | Ablehnen |
Algorithmus „none“ algNone | Ablehnen |
Algorithmus HS256, mit dem öffentlichen Schlüssel signiert hs256 | Ablehnen |
Unbekannter Schlüssel (kid) unknownKid | Ablehnen |
Pseudonym weicht von dem der Anzeige ab badHandle | Ablehnen |
{
"issuer": "https://test-vectors.qevik.invalid",
"audience": "plt_TESTVECTORS0001",
"verifyAt": 1792670400,
"listingHandle": "jean_d",
"jwks": {
"keys": [
{
"kty": "EC",
"crv": "P-256",
"x": "u_zfhmLEuPswg6JpT7grZpo4m_NkCFxmicl5x4FsUvY",
"y": "mzG_VCMoJIHUojBIijnhn6JmEUKPoXN05gmL31A3nUY",
"kid": "qevik-test-vectors-2026-10-22",
"use": "sig",
"alg": "ES256"
}
]
},
"vectors": [
{
"case": "valid",
"expected": "accept",
"token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InFldmlrLXRlc3QtdmVjdG9ycy0yMDI2LTEwLTIyIiwidHlwIjoicWV2aWstYXR0ZXN0YXRpb24rand0In0.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTMDAwMSIsInN1YiI6InRlc3QtdmVjdG9ycy1wYWlyd2lzZS1zdWIiLCJpYXQiOjE3OTI2NjY4MDAsIm5iZiI6MTc5MjY2NjgwMCwiZXhwIjoxNzkzMjcxNjAwLCJqdGkiOiJ0ZXN0LXZlY3RvcnMtanRpIiwiYXR0IjoiYWNjb3VudCIsIm1lbWJlciI6dHJ1ZSwic2luY2UiOjIwMjUsImRvbSI6ImV4YW1wbGUuY29tIiwicGxhdGZvcm1IYW5kbGUiOiJqZWFuX2QifQ.LWxRJPwTJUIZZZ1upll0EBR4UNubVjLtMgP8aNZQ2DiatLLz3tNqPc6RKvSi1cm14NVFYZukls7WCgy0nahOVQ"
},
{
"case": "expired",
"expected": "reject",
"token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InFldmlrLXRlc3QtdmVjdG9ycy0yMDI2LTEwLTIyIiwidHlwIjoicWV2aWstYXR0ZXN0YXRpb24rand0In0.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTMDAwMSIsInN1YiI6InRlc3QtdmVjdG9ycy1wYWlyd2lzZS1zdWIiLCJpYXQiOjE3OTE4MDY0MDAsIm5iZiI6MTc5MTgwNjQwMCwiZXhwIjoxNzkyNTg0MDAwLCJqdGkiOiJ0ZXN0LXZlY3RvcnMtanRpIiwiYXR0IjoiYWNjb3VudCIsIm1lbWJlciI6dHJ1ZSwic2luY2UiOjIwMjUsImRvbSI6ImV4YW1wbGUuY29tIiwicGxhdGZvcm1IYW5kbGUiOiJqZWFuX2QifQ.m73cnY6lVdJBaZmsfvjCKIVmQhD4wSIKFBaD8iyD9FRrpVxNiNiPfHTTQvEq095J0QKAAF5kb_kOIZLQDNwlVA"
},
{
"case": "badSignature",
"expected": "reject",
"token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InFldmlrLXRlc3QtdmVjdG9ycy0yMDI2LTEwLTIyIiwidHlwIjoicWV2aWstYXR0ZXN0YXRpb24rand0In0.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTMDAwMSIsInN1YiI6InRlc3QtdmVjdG9ycy1wYWlyd2lzZS1zdWIiLCJpYXQiOjE3OTI2NjY4MDAsIm5iZiI6MTc5MjY2NjgwMCwiZXhwIjoxNzkzMjcxNjAwLCJqdGkiOiJ0ZXN0LXZlY3RvcnMtanRpIiwiYXR0IjoiYWNjb3VudCIsIm1lbWJlciI6dHJ1ZSwic2luY2UiOjIwMjUsImRvbSI6ImV4YW1wbGUuY29tIiwicGxhdGZvcm1IYW5kbGUiOiJqZWFuX2QifQ.AWxRJPwTJUIZZZ1upll0EBR4UNubVjLtMgP8aNZQ2DiatLLz3tNqPc6RKvSi1cm14NVFYZukls7WCgy0nahOVQ"
},
{
"case": "wrongAud",
"expected": "reject",
"token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InFldmlrLXRlc3QtdmVjdG9ycy0yMDI2LTEwLTIyIiwidHlwIjoicWV2aWstYXR0ZXN0YXRpb24rand0In0.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTQ0xPU0VEIiwic3ViIjoidGVzdC12ZWN0b3JzLXBhaXJ3aXNlLXN1YiIsImlhdCI6MTc5MjY2NjgwMCwibmJmIjoxNzkyNjY2ODAwLCJleHAiOjE3OTMyNzE2MDAsImp0aSI6InRlc3QtdmVjdG9ycy1qdGkiLCJhdHQiOiJhY2NvdW50IiwibWVtYmVyIjp0cnVlLCJzaW5jZSI6MjAyNSwiZG9tIjoiZXhhbXBsZS5jb20iLCJwbGF0Zm9ybUhhbmRsZSI6ImplYW5fZCJ9.Ci2dObZufGWY3v-DyWWrrYFaDrqdX_C1elzyrkOm-NslEY5zHWyXhFF9-W-T8n9PO55_eJD0BeRzAgg_9qHHiQ"
},
{
"case": "algNone",
"expected": "reject",
"token": "eyJhbGciOiJub25lIiwia2lkIjoicWV2aWstdGVzdC12ZWN0b3JzLTIwMjYtMTAtMjIiLCJ0eXAiOiJxZXZpay1hdHRlc3RhdGlvbitqd3QifQ.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTMDAwMSIsInN1YiI6InRlc3QtdmVjdG9ycy1wYWlyd2lzZS1zdWIiLCJpYXQiOjE3OTI2NjY4MDAsIm5iZiI6MTc5MjY2NjgwMCwiZXhwIjoxNzkzMjcxNjAwLCJqdGkiOiJ0ZXN0LXZlY3RvcnMtanRpIiwiYXR0IjoiYWNjb3VudCIsIm1lbWJlciI6dHJ1ZSwic2luY2UiOjIwMjUsImRvbSI6ImV4YW1wbGUuY29tIiwicGxhdGZvcm1IYW5kbGUiOiJqZWFuX2QifQ."
},
{
"case": "hs256",
"expected": "reject",
"token": "eyJhbGciOiJIUzI1NiIsImtpZCI6InFldmlrLXRlc3QtdmVjdG9ycy0yMDI2LTEwLTIyIiwidHlwIjoicWV2aWstYXR0ZXN0YXRpb24rand0In0.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTMDAwMSIsInN1YiI6InRlc3QtdmVjdG9ycy1wYWlyd2lzZS1zdWIiLCJpYXQiOjE3OTI2NjY4MDAsIm5iZiI6MTc5MjY2NjgwMCwiZXhwIjoxNzkzMjcxNjAwLCJqdGkiOiJ0ZXN0LXZlY3RvcnMtanRpIiwiYXR0IjoiYWNjb3VudCIsIm1lbWJlciI6dHJ1ZSwic2luY2UiOjIwMjUsImRvbSI6ImV4YW1wbGUuY29tIiwicGxhdGZvcm1IYW5kbGUiOiJqZWFuX2QifQ.dfPx7WKEdyHAkjyATNTFF3M1D1URSC56PfrrnHFYw8c"
},
{
"case": "unknownKid",
"expected": "reject",
"token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InFldmlrLXRlc3QtdmVjdG9ycy11bmtub3duIiwidHlwIjoicWV2aWstYXR0ZXN0YXRpb24rand0In0.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTMDAwMSIsInN1YiI6InRlc3QtdmVjdG9ycy1wYWlyd2lzZS1zdWIiLCJpYXQiOjE3OTI2NjY4MDAsIm5iZiI6MTc5MjY2NjgwMCwiZXhwIjoxNzkzMjcxNjAwLCJqdGkiOiJ0ZXN0LXZlY3RvcnMtanRpIiwiYXR0IjoiYWNjb3VudCIsIm1lbWJlciI6dHJ1ZSwic2luY2UiOjIwMjUsImRvbSI6ImV4YW1wbGUuY29tIiwicGxhdGZvcm1IYW5kbGUiOiJqZWFuX2QifQ.7BmSiWeOXySvC4AkJfRaRNXyOURPsgmoK1IHKEyzUydmg2cHK5o0JH9gAd01SaYBecdMKbzJ4Hhy4Vpnt4w69g"
},
{
"case": "badHandle",
"expected": "reject",
"token": "eyJhbGciOiJFUzI1NiIsImtpZCI6InFldmlrLXRlc3QtdmVjdG9ycy0yMDI2LTEwLTIyIiwidHlwIjoicWV2aWstYXR0ZXN0YXRpb24rand0In0.eyJpc3MiOiJodHRwczovL3Rlc3QtdmVjdG9ycy5xZXZpay5pbnZhbGlkIiwiYXVkIjoicGx0X1RFU1RWRUNUT1JTMDAwMSIsInN1YiI6InRlc3QtdmVjdG9ycy1wYWlyd2lzZS1zdWIiLCJpYXQiOjE3OTI2NjY4MDAsIm5iZiI6MTc5MjY2NjgwMCwiZXhwIjoxNzkzMjcxNjAwLCJqdGkiOiJ0ZXN0LXZlY3RvcnMtanRpIiwiYXR0IjoiYWNjb3VudCIsIm1lbWJlciI6dHJ1ZSwic2luY2UiOjIwMjUsImRvbSI6ImV4YW1wbGUuY29tIiwicGxhdGZvcm1IYW5kbGUiOiJtYXJpZV9rIn0.qVAM8tm0AMoL_npDpHe5JmTXOFzutksNPWwbDpbm0ySHCIQ0EUrLD_otABxiNTRaDFndF7XjhPaQ24NLO5U_eg"
}
]
}Das lesen Ihre Besucher auf der Prüfseite von Qevik:
Was dieses Abzeichen nicht belegt
Qevik prüft nicht die Identität von Personen. Dieses Abzeichen bürgt weder für die Zuverlässigkeit seines Inhabers noch für seine Anzeigen oder die Gegenstände und Pakete, die er befördert, und Qevik versichert nichts. Es verspricht nicht, dass ein verlorener Gegenstand wiedergefunden wird. Qevik ist an Geschäften auf der Website, die das Abzeichen zeigt, nicht beteiligt.
API-Nutzungsbedingungen (Version 1.0)(neuer Tab)
Die API ist kostenlos und wird ohne Service-Level bereitgestellt. Individueller Support wird nicht zugesagt.