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):
- After checkout, create an enrolment token for the ticket and render the widget on the confirmation page.
- 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'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