Skip to content

We use technical cookies to make the website work. With your consent we would also use cookies to remember your preferences, measure visits and show you more relevant ads. You can change your mind at any time from the bottom of the page. Read the cookie policy

API and webhooks for developers

Connect your website, your management software or your apps to your Yuppi account. API keys and webhooks are included in the Pro and Agency Suite plans, at no extra cost.

Contract being implemented

This is version 1 of the interface, still being built: addresses and fields may change before the launch. Every change will be noted on this page.

Authentication

Create a key in the dashboard, under “Calendar, API and webhooks”. Give each key a name and only the permissions it needs.

The full key is shown only once, when you create it: we store it so that not even we can read it again. If you lose it, revoke it and create another one.

Send it with every request in the Authorization header. Use it only from your server: never in the code of a web page or an app others can download.

Header
Authorization: Bearer yk_live_...
Permissions
PermissionWhat it allows
bookings:readRead the bookings
bookings:writeConfirm, decline and update the bookings
calendar:readRead the calendar, including the busy times imported from your calendar
clients:readRead the clients
quotes:readRead the quotes
services:readRead the services

Address and formats

Base address
https://yuppi.now/v1
  • Requests and responses are JSON (UTF-8), with camelCase field names.
  • Days use the “YYYY-MM-DD” format; instants are ISO 8601 with offset, for example “2026-10-10T14:00:00+02:00”.
  • Amounts are euro cents, VAT included: 28000 means €280.00.
  • Lists come in pages: ?page=1, and the response has items, page, pageSize and total.
  • Writes (POST) carry an Idempotency-Key header with a unique value: if you repeat the same request, it is not run twice.
Example request
curl "https://yuppi.now/v1/provider/bookings?status=pending&page=1" \
  -H "Authorization: Bearer yk_live_..." \
  -H "Accept: application/json"

Errors

Errors follow RFC 9457 (application/problem+json): the HTTP status, a stable code to use in your code and, for invalid data, errors with one message per field.

401
key missing, wrong or revoked
403
insufficient_scope: the key lacks the permission; addon_required: the plan does not include the tools
404
not found, or not in your account
409
the state does not allow it, for example a booking already confirmed
422
invalid data
429
too many requests: wait the seconds given in Retry-After
application/problem+json
{
  "type": "about:blank",
  "title": "Forbidden",
  "status": 403,
  "detail": "La chiave non ha il permesso bookings:read.",
  "code": "insufficient_scope",
  "requestId": "req_7f3a9c2e"
}

Main endpoints

They read and write only the data of your account. The examples show the format of the responses, with sample data.

  • GET /provider/bookings?status=&page=

    Bookings, newest first; status: pending, confirmed, declined, cancelled, completed

    Permission: bookings:read

    Example response
    {
      "items": [
        {
          "id": "bkg_m3x8q2",
          "code": "YU-7KQ2XH",
          "status": "confirmed",
          "service": {
            "id": "svc_4r9t1c",
            "title": "Festa di compleanno con il mago"
          },
          "package": {
            "name": "Festa completa",
            "durationMinutes": 120,
            "maxChildren": 25
          },
          "date": "2026-10-17",
          "startTime": "16:00",
          "address": "Via Toledo 120",
          "town": "Napoli",
          "children": 18,
          "ageBand": "4-7",
          "notes": "Festa a tema pirati, torta alle 17:30",
          "customer": {
            "id": "cus_8h2k4d",
            "name": "Giulia Esposito",
            "email": "giulia@example.com",
            "phone": "+39 333 000 0000"
          },
          "totalCents": 28000,
          "createdAt": "2026-09-20T10:12:00+02:00",
          "respondBy": "2026-09-21T10:12:00+02:00",
          "declineReason": null,
          "timeline": [
            {
              "at": "2026-09-20T10:12:00+02:00",
              "type": "created",
              "by": "customer",
              "note": null
            },
            {
              "at": "2026-09-20T11:03:00+02:00",
              "type": "confirmed",
              "by": "provider",
              "note": null
            }
          ],
          "assignees": [
            {
              "id": "stf_2p7w9z",
              "name": "Marco Russo"
            }
          ],
          "entertainersNeeded": 1,
          "deposit": {
            "amountCents": 8400,
            "status": "paid",
            "paidAt": "2026-09-20T18:40:00+02:00"
          },
          "balance": {
            "amountCents": 19600,
            "status": "due",
            "paidAt": null
          },
          "dispute": null,
          "noShow": null,
          "canMarkNoShow": false,
          "canDispute": false
        }
      ],
      "page": 1,
      "pageSize": 20,
      "total": 1
    }
  • GET /provider/bookings/{id}

    One booking, with history and deposit

    Permission: bookings:read

  • POST /provider/bookings/{id}/confirm

    Confirms a pending booking

    Permission: bookings:write

    Confirming a booking: depositPercent (10-50) asks the family for a deposit; agencies can pass assigneeIds, who runs the party.

    Example request
    curl -X POST "https://yuppi.now/v1/provider/bookings/bkg_m3x8q2/confirm" \
      -H "Authorization: Bearer yk_live_..." \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: 4f1c2a9e-8d3b-4f6a-9c1e-2b7d5a0e3f18" \
      -d '{ "depositPercent": 30 }'
  • POST /provider/bookings/{id}/decline

    Declines a pending booking, with a reason and a message

    Permission: bookings:write

  • GET /provider/calendar?month=YYYY-MM

    The calendar of a month: parties, events added by hand, imported busy times (source: external) and blocked days

    Permission: calendar:read

    Example response
    {
      "month": "2026-10",
      "entries": [
        {
          "id": "bkg_m3x8q2",
          "source": "booking",
          "bookingId": "bkg_m3x8q2",
          "code": "YU-7KQ2XH",
          "status": "confirmed",
          "date": "2026-10-17",
          "startTime": "16:00",
          "durationMinutes": 120,
          "customerName": "Giulia Esposito",
          "serviceTitle": "Festa di compleanno con il mago",
          "town": "Napoli",
          "assignees": [
            {
              "id": "stf_2p7w9z",
              "name": "Marco Russo"
            }
          ],
          "entertainersNeeded": 1,
          "venueId": null
        },
        {
          "id": "ext_9d1f6b",
          "source": "external",
          "bookingId": null,
          "code": null,
          "status": "confirmed",
          "date": "2026-10-20",
          "startTime": "10:00",
          "durationMinutes": 90,
          "customerName": "",
          "serviceTitle": "Impegno privato",
          "town": "",
          "assignees": [],
          "entertainersNeeded": 0,
          "venueId": null
        }
      ],
      "blocks": [
        {
          "date": "2026-10-25",
          "note": "Ferie",
          "managedBy": "provider"
        }
      ],
      "calendarMode": "self_managed"
    }
  • GET /provider/clients?q=&page=

    Clients, with number of parties and total spent

    Permission: clients:read

    Example response
    {
      "items": [
        {
          "id": "cus_8h2k4d",
          "source": "site",
          "name": "Giulia Esposito",
          "email": "giulia@example.com",
          "phone": "+39 333 000 0000",
          "town": "Napoli",
          "bookings": 2,
          "lastPartyDate": "2026-10-17",
          "totalCents": 52000
        }
      ],
      "page": 1,
      "pageSize": 20,
      "total": 1
    }
  • GET /provider/quotes?status=&page=

    Quotes; status: draft, sent, accepted, declined, expired

    Permission: quotes:read

    Example response
    {
      "items": [
        {
          "id": "quo_5b8n3v",
          "number": "P-2026-014",
          "status": "sent",
          "client": {
            "id": "cli_1s6j0e",
            "name": "Anna De Luca",
            "email": "anna@example.com",
            "phone": "+39 347 000 0000"
          },
          "title": "Comunione di Sofia: animazione e truccabimbi",
          "eventDate": "2026-11-08",
          "totalCents": 45000,
          "validUntil": "2026-10-08",
          "sentAt": "2026-09-24T09:30:00+02:00",
          "outcome": null
        }
      ],
      "page": 1,
      "pageSize": 20,
      "total": 1
    }
  • GET /provider/services

    Your services, with status and statistics of the last 30 days

    Permission: services:read

    Example response
    [
      {
        "id": "svc_4r9t1c",
        "slug": "festa-di-compleanno-con-il-mago",
        "title": "Festa di compleanno con il mago",
        "category": "feste-bambini",
        "status": "published",
        "fromPriceCents": 18000,
        "stats": {
          "views30d": 412,
          "requests30d": 9
        },
        "sponsorship": null,
        "pendingChanges": false,
        "photo": {
          "id": "pho_3k7m2x",
          "url": "https://yuppi.now/media/pho_3k7m2x.webp",
          "alt": "Il mago con i bambini"
        }
      }
    ]

Webhooks

Add a webhook in the dashboard: choose the https address that receives the calls and the events you care about. At most 5 webhooks per account.

For each event Yuppi sends a POST request with a JSON body: event id, type, instant and the details in data. With “Test” you receive a ping event.

Webhooks
EventWhen it arrives
booking.createdA family sends a booking request
booking.confirmedYou confirm a booking
booking.cancelledA booking is cancelled, by you or by the family
quote.acceptedThe client accepts a quote
quote.declinedThe client declines a quote
deposit.paidA deposit is paid
review.publishedA review of one of your services is published
pingYou press “Test” in the dashboard
Example: booking.confirmed
{
  "id": "evt_2w9c4r",
  "type": "booking.confirmed",
  "createdAt": "2026-09-20T11:03:02+02:00",
  "data": {
    "booking": {
      "id": "bkg_m3x8q2",
      "code": "YU-7KQ2XH",
      "status": "confirmed",
      "service": {
        "id": "svc_4r9t1c",
        "title": "Festa di compleanno con il mago"
      },
      "package": {
        "name": "Festa completa",
        "durationMinutes": 120,
        "maxChildren": 25
      },
      "date": "2026-10-17",
      "startTime": "16:00",
      "address": "Via Toledo 120",
      "town": "Napoli",
      "children": 18,
      "ageBand": "4-7",
      "notes": "Festa a tema pirati, torta alle 17:30",
      "customer": {
        "id": "cus_8h2k4d",
        "name": "Giulia Esposito",
        "email": "giulia@example.com",
        "phone": "+39 333 000 0000"
      },
      "totalCents": 28000,
      "createdAt": "2026-09-20T10:12:00+02:00",
      "respondBy": "2026-09-21T10:12:00+02:00",
      "declineReason": null,
      "timeline": [
        {
          "at": "2026-09-20T10:12:00+02:00",
          "type": "created",
          "by": "customer",
          "note": null
        },
        {
          "at": "2026-09-20T11:03:00+02:00",
          "type": "confirmed",
          "by": "provider",
          "note": null
        }
      ],
      "assignees": [
        {
          "id": "stf_2p7w9z",
          "name": "Marco Russo"
        }
      ],
      "entertainersNeeded": 1,
      "deposit": {
        "amountCents": 8400,
        "status": "paid",
        "paidAt": "2026-09-20T18:40:00+02:00"
      },
      "balance": {
        "amountCents": 19600,
        "status": "due",
        "paidAt": null
      },
      "dispute": null,
      "noShow": null,
      "canMarkNoShow": false,
      "canDispute": false
    }
  }
}
Example: review.published
{
  "id": "evt_8m1q5z",
  "type": "review.published",
  "createdAt": "2026-10-19T09:00:00+02:00",
  "data": {
    "review": {
      "id": "rev_6t2y8u",
      "author": "Giulia E.",
      "occasion": "Compleanno",
      "partyDate": "2026-10-17",
      "rating": 5,
      "title": "Festa perfetta",
      "body": "Bambini entusiasti dall’inizio alla fine.",
      "reply": null
    },
    "serviceId": "svc_4r9t1c"
  }
}

Checking the signature

Every call carries the Yuppi-Signature header: the HMAC-SHA256 of the request body, exactly as it arrives, computed with the webhook secret and written in lowercase hexadecimal.

The secret is shown in full only once, when you create the webhook. Compute the signature on the raw body, before reading the JSON, and compare it in constant time. If it does not match, answer 401 and ignore the call.

Header
Yuppi-Signature: 3f9a0c6d1e...b27e
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';

// rawBody: the body exactly as received (Buffer), before any JSON parsing
export function isFromYuppi(rawBody, signature, secret) {
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(String(signature ?? ''));
  return a.length === b.length && timingSafeEqual(a, b);
}
PHP
<?php
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', $body, getenv('YUPPI_WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_YUPPI_SIGNATURE'] ?? '')) {
  http_response_code(401);
  exit;
}
$event = json_decode($body, true);
http_response_code(200);

Answers and retries

Answer with a 2xx code within 10 seconds: do the long work after answering.

If your address does not answer or answers with an error, we try again with longer and longer waits for 24 hours. In the dashboard you see the last 30 deliveries, with status and duration.

The same event can arrive more than once or out of order: use the event id to avoid handling it twice and the createdAt instant for the order.

Limits

  • 60 requests per minute per key. Beyond that, we answer 429 with Retry-After.
  • At most 10 API keys and 5 webhooks per account.
  • Keys and webhooks work while the Pro or Agency Suite plan is active.