faceping.aifaceping.aidocs

Examples

Copy-paste integrations for popular frameworks — create a token at checkout, show the widget, handle withdrawals.

Every integration does the same two things on your server, with your API key kept in an environment variable (FACEPING_API_KEY):

  1. After checkout, create an enrolment token for the ticket and render the widget on the confirmation page.
  2. When a guest withdraws consent or asks for erasure, remove their enrolment.

Pick your stack — the choice is remembered across the docs.

Show the widget after checkout#

// npm i express — Node 18+ has fetch built in
import express from "express";
const app = express();
const FACEPING = "https://api-eu.faceping.ai";

async function enrolmentToken(eventId, ticketId) {
  const res = await fetch(`${FACEPING}/v1/events/${eventId}/enrolment-tokens`, {
    method: "POST",
    headers: { "X-Api-Key": process.env.FACEPING_API_KEY, "Content-Type": "application/json" },
    body: JSON.stringify({ ticketId }),
  });
  if (!res.ok) throw new Error(`FacePing ${res.status}: ${(await res.json()).message}`);
  return (await res.json()).token;
}

app.get("/orders/:id/confirmation", async (req, res) => {
  const order = await orders.find(req.params.id);             // your code
  const token = await enrolmentToken(order.faceEventId, order.ticketNumber);
  res.send(`
    <h1>You're going!</h1>
    <div data-faceping-token="${token}"></div>
    <script src="https://faceping.ai/widget.js" async></script>`);
});
// app/orders/[id]/confirmation/page.tsx — a Server Component, so the key never reaches the browser
const FACEPING = "https://api-eu.faceping.ai";

async function enrolmentToken(eventId: string, ticketId: string) {
  const res = await fetch(`${FACEPING}/v1/events/${eventId}/enrolment-tokens`, {
    method: "POST",
    headers: { "X-Api-Key": process.env.FACEPING_API_KEY!, "Content-Type": "application/json" },
    body: JSON.stringify({ ticketId }),
    cache: "no-store",
  });
  if (!res.ok) throw new Error(`FacePing ${res.status}`);
  return (await res.json()).token as string;
}

export default async function Confirmation({ params }: { params: Promise<{ id: string }> }) {
  const order = await getOrder((await params).id);            // your code
  const token = await enrolmentToken(order.faceEventId, order.ticketNumber);
  return (
    <>
      <h1>You&apos;re going!</h1>
      <div data-faceping-token={token} />
      <script src="https://faceping.ai/widget.js" async />
    </>
  );
}
# pip install flask requests
import os, requests
from flask import Flask, render_template_string

app = Flask(__name__)
FACEPING = "https://api-eu.faceping.ai"

def enrolment_token(event_id: str, ticket_id: str) -> str:
    r = requests.post(f"{FACEPING}/v1/events/{event_id}/enrolment-tokens",
                      headers={"X-Api-Key": os.environ["FACEPING_API_KEY"]},
                      json={"ticketId": ticket_id}, timeout=15)
    r.raise_for_status()
    return r.json()["token"]

@app.get("/orders/<order_id>/confirmation")
def confirmation(order_id):
    order = get_order(order_id)                                 # your code
    token = enrolment_token(order.face_event_id, order.ticket_number)
    return render_template_string("""
      <h1>You're going!</h1>
      <div data-faceping-token="{{ token }}"></div>
      <script src="https://faceping.ai/widget.js" async></script>""", token=token)
# views.py — pip install requests
import os, requests
from django.shortcuts import get_object_or_404, render
from .models import Order

FACEPING = "https://api-eu.faceping.ai"

def enrolment_token(event_id, ticket_id):
    r = requests.post(f"{FACEPING}/v1/events/{event_id}/enrolment-tokens",
                      headers={"X-Api-Key": os.environ["FACEPING_API_KEY"]},
                      json={"ticketId": ticket_id}, timeout=15)
    r.raise_for_status()
    return r.json()["token"]

def confirmation(request, order_id):
    order = get_object_or_404(Order, pk=order_id, user=request.user)
    token = enrolment_token(order.face_event_id, order.ticket_number)
    return render(request, "orders/confirmation.html", {"faceping_token": token})

# orders/confirmation.html:
#   <div data-faceping-token="{{ faceping_token }}"></div>
#   <script src="https://faceping.ai/widget.js" async></script>
<?php
// app/Http/Controllers/OrderConfirmationController.php
namespace App\Http\Controllers;

use App\Models\Order;
use Illuminate\Support\Facades\Http;

class OrderConfirmationController extends Controller
{
    private const FACEPING = 'https://api-eu.faceping.ai';

    public function __invoke(Order $order)
    {
        $token = Http::withHeaders(['X-Api-Key' => config('services.faceping.key')])
            ->post(self::FACEPING . "/v1/events/{$order->face_event_id}/enrolment-tokens", [
                'ticketId' => $order->ticket_number,
            ])
            ->throw()
            ->json('token');

        return view('orders.confirmation', ['facepingToken' => $token]);
    }
}

// config/services.php: 'faceping' => ['key' => env('FACEPING_API_KEY')],
// resources/views/orders/confirmation.blade.php:
//   <div data-faceping-token="{{ $facepingToken }}"></div>
//   <script src="https://faceping.ai/widget.js" async></script>
// Program.cs — register once:
// builder.Services.AddHttpClient("faceping", c => {
//     c.BaseAddress = new Uri("https://api-eu.faceping.ai/");
//     c.DefaultRequestHeaders.Add("X-Api-Key", builder.Configuration["FACEPING_API_KEY"]);
// });

app.MapGet("/orders/{id}/confirmation", async (string id, IHttpClientFactory http, OrderStore orders) =>
{
    var order = await orders.FindAsync(id);                     // your code
    var res = await http.CreateClient("faceping").PostAsJsonAsync(
        $"v1/events/{order.FaceEventId}/enrolment-tokens", new { ticketId = order.TicketNumber });
    res.EnsureSuccessStatusCode();
    var token = (await res.Content.ReadFromJsonAsync<TokenResponse>())!.Token;

    return Results.Content($"""
        <h1>You're going!</h1>
        <div data-faceping-token="{token}"></div>
        <script src="https://faceping.ai/widget.js" async></script>
        """, "text/html");
});

record TokenResponse(string Token, DateTimeOffset ExpiresAt, string Url);
// ConfirmationController.java — Spring Boot 3.2+ (RestClient)
@Controller
class ConfirmationController {
    private final RestClient faceping;
    private final OrderRepository orders;

    ConfirmationController(@Value("${FACEPING_API_KEY}") String apiKey, OrderRepository orders) {
        this.orders = orders;
        this.faceping = RestClient.builder()
            .baseUrl("https://api-eu.faceping.ai")
            .defaultHeader("X-Api-Key", apiKey)
            .build();
    }

    record TokenResponse(String token, String expiresAt, String url) {}

    @GetMapping("/orders/{id}/confirmation")
    String confirmation(@PathVariable String id, Model model) {
        var order = orders.findById(id).orElseThrow();
        var token = faceping.post()
            .uri("/v1/events/{eventId}/enrolment-tokens", order.getFaceEventId())
            .contentType(MediaType.APPLICATION_JSON)
            .body(Map.of("ticketId", order.getTicketNumber()))
            .retrieve()
            .body(TokenResponse.class);
        model.addAttribute("facepingToken", token.token());
        return "confirmation"; // <div th:attr="data-faceping-token=${facepingToken}"></div> + the widget.js script tag
    }
}
// Go 1.22+ (net/http routing with path values)
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "html/template"
    "net/http"
    "net/url"
    "os"
)

const faceping = "https://api-eu.faceping.ai"

func enrolmentToken(eventID, ticketID string) (string, error) {
    body, _ := json.Marshal(map[string]string{"ticketId": ticketID})
    req, _ := http.NewRequest("POST", fmt.Sprintf("%s/v1/events/%s/enrolment-tokens", faceping, eventID), bytes.NewReader(body))
    req.Header.Set("X-Api-Key", os.Getenv("FACEPING_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    res, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer res.Body.Close()
    if res.StatusCode != http.StatusOK {
        return "", fmt.Errorf("faceping: %s", res.Status)
    }
    var out struct{ Token string `json:"token"` }
    if err := json.NewDecoder(res.Body).Decode(&out); err != nil {
        return "", err
    }
    return out.Token, nil
}

var page = template.Must(template.New("c").Parse(`<h1>You're going!</h1>
<div data-faceping-token="{{.}}"></div>
<script src="https://faceping.ai/widget.js" async></script>`))

func main() {
    http.HandleFunc("GET /orders/{id}/confirmation", func(w http.ResponseWriter, r *http.Request) {
        order := findOrder(r.PathValue("id")) // your code
        token, err := enrolmentToken(order.FaceEventID, order.TicketNumber)
        if err != nil {
            http.Error(w, "face check-in unavailable", http.StatusBadGateway)
            return
        }
        page.Execute(w, token)
    })
    http.ListenAndServe(":8080", nil)
}
# app/controllers/confirmations_controller.rb
require "net/http"

class ConfirmationsController < ApplicationController
  FACEPING = URI("https://api-eu.faceping.ai")

  def show
    order = current_user.orders.find(params[:order_id])
    @faceping_token = enrolment_token(order.face_event_id, order.ticket_number)
  end

  private

  def enrolment_token(event_id, ticket_id)
    uri = FACEPING.dup.tap { |u| u.path = "/v1/events/#{event_id}/enrolment-tokens" }
    res = Net::HTTP.post(uri, { ticketId: ticket_id }.to_json,
                         "X-Api-Key" => ENV.fetch("FACEPING_API_KEY"), "Content-Type" => "application/json")
    raise "FacePing #{res.code}" unless res.is_a?(Net::HTTPSuccess)
    JSON.parse(res.body).fetch("token")
  end
end

# app/views/confirmations/show.html.erb:
#   <div data-faceping-token="<%= @faceping_token %>"></div>
#   <script src="https://faceping.ai/widget.js" async></script>

POST /v1/events/{eventId}/enrolment-tokens →

Remove a guest's face data#

Call this when a guest withdraws consent or asks for erasure — for example from your account page or support tooling.

await fetch(`${FACEPING}/v1/events/${eventId}/enrolments/${encodeURIComponent(ticketId)}`, {
  method: "DELETE",
  headers: { "X-Api-Key": process.env.FACEPING_API_KEY },
}); // 204 = removed, 404 = no enrolment for that ticket
// app/actions.ts
"use server";
export async function removeFaceData(eventId: string, ticketId: string) {
  await fetch(`https://api-eu.faceping.ai/v1/events/${eventId}/enrolments/${encodeURIComponent(ticketId)}`, {
    method: "DELETE",
    headers: { "X-Api-Key": process.env.FACEPING_API_KEY! },
  });
}
from urllib.parse import quote
requests.delete(f"{FACEPING}/v1/events/{event_id}/enrolments/{quote(ticket_id, safe='')}",
                headers={"X-Api-Key": os.environ["FACEPING_API_KEY"]}, timeout=15)
from urllib.parse import quote
requests.delete(f"{FACEPING}/v1/events/{event_id}/enrolments/{quote(ticket_id, safe='')}",
                headers={"X-Api-Key": os.environ["FACEPING_API_KEY"]}, timeout=15)
Http::withHeaders(['X-Api-Key' => config('services.faceping.key')])
    ->delete('https://api-eu.faceping.ai/v1/events/' . $eventId . '/enrolments/' . rawurlencode($ticketId));
await http.CreateClient("faceping").DeleteAsync($"v1/events/{eventId}/enrolments/{Uri.EscapeDataString(ticketId)}");
faceping.delete()
    .uri("/v1/events/{eventId}/enrolments/{ticketId}", eventId, ticketId) // URI template encodes ticketId
    .retrieve()
    .toBodilessEntity();
req, _ := http.NewRequest("DELETE",
    fmt.Sprintf("%s/v1/events/%s/enrolments/%s", faceping, eventID, url.PathEscape(ticketID)), nil)
req.Header.Set("X-Api-Key", os.Getenv("FACEPING_API_KEY"))
res, err := http.DefaultClient.Do(req)
uri = URI("https://api-eu.faceping.ai/v1/events/#{event_id}/enrolments/#{ERB::Util.url_encode(ticket_id)}")
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  http.delete(uri.path, "X-Api-Key" => ENV.fetch("FACEPING_API_KEY"))
end

DELETE /v1/events/{eventId}/enrolments/{ticketId} →