Documentation

Integration code

Add the code below to your backend project as a single file. All Touchque communication runs through this file. Choose your preferred language.

This file contains 2 sections:
1) Protect a route with the requireTouchque middleware — it answers 202 until approved
2) Read the approval off req.touchque once the handler runs
Endpoint
PathFunction
POST /api/loginProtected by requireTouchque('LOGIN')
POST /api/transferProtected by requireTouchque('SEND_MONEY')
The approval is bound to one user + action + transaction and can be consumed exactly once — a token can't be replayed for a different amount, recipient or user.
Express integration file
const express = require('express');
const { requireTouchque } = require('@touchque/node');

const app = express();
app.use(express.json());

// ─── TQ_API_KEY / TQ_API_SECRET are read from the environment ───

// ═══════════════════════════════════════
// 1) Sensitive Routes — One-Line Middleware!
// ═══════════════════════════════════════

// Request 2FA approval for login
app.post(
  '/api/login',
  requireTouchque('LOGIN', { user: (req) => req.session.passwordOk }),
  (req, res) => {
    // ✅ Only reached once the user approved on their phone.
    res.json({ success: true, assurance: req.touchque.assurance });
  }
);

// Request 2FA approval for a money transfer (with transaction details)
app.post(
  '/api/transfer',
  requireTouchque('SEND_MONEY', {
    user: (req) => req.session.user?.email,
    details: (req) => ({ Amount: `${req.body.amount} EUR`, To: req.body.iban }),
  }),
  (req, res) => {
    res.json({ success: true, message: 'Transfer completed!' });
  }
);

// ═══════════════════════════════════════
// 2) What actually happens under the hood?
// ═══════════════════════════════════════
// a) The first request answers 202 { touchque: step, token } (no approval yet).
// b) Your frontend shows step.number (or step.enroll.qrCodeDataUrl on first link) in
//    its own design, then resends the same request with header X-Touchque-Token
//    (@touchque/web's touchqueFetch() does this loop for you).
// c) Once approved, the handler above runs EXACTLY ONCE.

app.listen(3000, () => console.log('Server: http://localhost:3000'));