---
title: "Import a quote signed on a booking platform"
method: POST
path: "/events/{id}/quotes/import"
tags: ["Quotes"]
---

# Import a quote signed on a booking platform

`POST /events/{id}/quotes/import`

## Request

- Base URL: `https://joinways.app/api/v1`
- URL: `https://joinways.app/api/v1/events/{id}/quotes/import`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://joinways.app/oauth/authorize`, token URL `https://joinways.app/api/oauth/token`, scopes: `mcp.write`)

Attaches a PDF signed on a booking platform (Kactus, Privateaser…) to the event as its signed quote. With `quote_id`, the existing Joinways quote of that event is stamped signed (`created: false`); without it, an external quote (`source: platform`) is created from the platform totals (`created: true`). Both outcomes confirm the event and write the activity, like a paper signature. The decoded PDF is capped at 4 MB. Same fields in both body formats. With an `Idempotency-Key`, a multipart retry is recognised by its fields and the SHA-256 of the file. **Role permission:** `quotes.manage`.

## Headers

- `Idempotency-Key` string

## Request body

- object: Fields of a signed platform quote import, shared by the multipart and JSON bodies. Amounts are only used when no `quote_id` is given.
  - `pdf_base64` string, byte, required: The signed PDF, base64-encoded (a `data:` prefix is accepted; 4 MB max once decoded).
  - `file_name` string: File name stored with the proof. Defaults to `devis-signe.pdf`.
  - `quote_id` string, uuid: Joinways quote of the event the PDF is the signed copy of. Omitted = an external quote is created.
  - `platform` string: Platform name.
  - `external_reference` string: The platform's own quote number. Importing it twice on the same event is refused (409).
  - `title` string
  - `currency` string
  - `total_excl_tax` number: External quote only. One of the two totals is enough; the other is derived from the venue VAT rate.
  - `total_incl_tax` number: External quote only. Must not be lower than `total_excl_tax`.
  - `signed_at` string, date-time: Defaults to now.
  - `signer_name` string
  - `signer_email` string, email

## Response `201`

Signature recorded.

- SignedQuoteImportResult
  - `quote_id` string, uuid, required: The signed quote (existing or newly created).
  - `created` boolean, required: true = an external quote row was created; false = the given `quote_id` was stamped signed.
  - `event_id` string, uuid, required
  - `signature_id` string, uuid, nullable
  - `event_status` string, nullable: Event status after the import (`confirmed` unless the event was already past that point).

## Other responses

- `400`: Validation failed (`invalid_request`). For a body that fails the schema, `details` holds `formErrors` and `fieldErrors`.
- `401`: Missing, invalid, expired or revoked bearer token (`unauthorized`).
- `402`: Quotes are not included in the workspace plan (`payment_required`, `details.reason` `quotes_plan_required`).
- `403`: `forbidden`: the token lacks the scope, its owner's role lacks the permission named by the operation, or the workspace has neither the Pro plan nor a trial (`details.reason` `plan_not_eligible`).
- `404`: Not found in this workspace, or the id of the path is not a UUID (`not_found`).
- `409`: The state of the record refuses the operation (`conflict`). `details.reason` is a stable code.
- `413`: The PDF exceeds 4 MB (`payload_too_large`).
- `429`: Too many requests (`rate_limited`). `details.reason`: `per-ip`, `per-account-minute`, `per-account-day`, or `email_rate_limited` (email budget). Always carries `Retry-After`: wait that many seconds.

---

[API](https://skmtc.dev/joinways/apis/joinways-public-api.md) · [All operations](https://skmtc.dev/joinways/apis/joinways-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/joinways/apis/joinways-public-api/revisions/39e84309953e?raw)
