Skip to content

Webhook endpoint — import an offer

The webhook endpoint creates a single offer as a draft in your Flino organisation. It is idempotent over the combination external_source + external_id, so the same call sent multiple times does not produce a duplicate.

POST https://<your-project>.supabase.co/functions/v1/zapier-offer-import

You find the <your-project> part in Settings → Integrations → API tokens, in the “Zapier Webhook URL” section.

This Supabase Functions URL is the canonical and permanent endpoint address for the webhook. A separate API subdomain (e.g. api.flino.io) is not planned at the moment — use the URL shown here as the stable address.

Headers:

x-flino-token: <your-api-token>
Content-Type: application/json

Generate <your-api-token> as described in API tokens. Per token the scope is fixed to offer_import:write.

Only POST is accepted. Other methods return 405 Method not allowed.

The body must be a JSON object. Arrays or strings are rejected with 400 Request body must be a JSON object.

FieldTypeDescription
titlestring, non-emptyOffer title, e.g. “Website redesign Mustermann GmbH”
external_idstring, non-emptyUnique ID from your system — the key for idempotency
external_sourcestring, non-emptySource system, e.g. zoho, pipedrive, zapier, myform — stored internally in lowercase
FieldTypeDescription / default
amountnumber | nullOffer amount in main currency unit (e.g. 1500.00)
currencystringEUR (default), USD, or CHF. Other values are rejected with 400.
contact_namestring | nullFull name. Flino splits it into first and last name automatically.
contact_emailstring | nullMust be in valid email format. Stored in lowercase.
company_namestring | nullCompany name; Flino creates a new company or reuses an existing one
offer_numberstring | nullYour own offer number
valid_untilstring | nullISO date, e.g. 2026-12-31. Invalid dates are ignored (value stays null).
  • Salutation and language are not transmitted in the Phase 1 webhook — they are added on the contact manually or in the first send dialog.
  • Address is not transmitted in the Phase 1 webhook.

For each call, Flino checks whether an offer with the same external_source + external_id already exists in your organisation.

  • If it exists: no new offer, response 200 with created: false and the existing offer_id.
  • If it does not exist: a new offer is created, response 200 with created: true.

Useful when your source system retries — you don’t end up with duplicates.

Status 200 OK:

{
"success": true,
"created": true,
"offer_id": "8d3f7e09-1e0e-44e5-b6c3-49e3a7c2a131",
"status": "draft"
}

On idempotency hit:

{
"success": true,
"created": false,
"offer_id": "8d3f7e09-1e0e-44e5-b6c3-49e3a7c2a131",
"status": "draft"
}
POST /functions/v1/zapier-offer-import HTTP/1.1
Host: <your-project>.supabase.co
Content-Type: application/json
x-flino-token: 9a3f...c1e2
{
"title": "Website redesign Mustermann GmbH",
"amount": 4900,
"currency": "EUR",
"contact_name": "Anna Mustermann",
"contact_email": "[email protected]",
"company_name": "Mustermann GmbH",
"offer_number": "ANG-2026-0042",
"valid_until": "2026-06-30",
"external_id": "deal_19238",
"external_source": "zoho"
}
{
"success": true,
"created": true,
"offer_id": "8d3f7e09-1e0e-44e5-b6c3-49e3a7c2a131",
"status": "draft"
}
HTTP statusBody errorMeaning
400Invalid JSON bodyBody is not valid JSON
400Request body must be a JSON objectBody is an array or a string
400Field 'title' is required and must be a non-empty stringtitle missing or empty
400Field 'external_id' is required and must be a non-empty stringexternal_id missing or empty
400Field 'external_source' is required and must be a non-empty stringexternal_source missing or empty
400Field 'currency' must be one of: EUR, USD, CHFunsupported currency
400Field 'amount' must be a numberamount sent as string or boolean
400Field 'contact_email' is not a valid email addressinvalid email format
401Missing x-flino-token headerHeader missing
401Invalid or unknown tokenToken not found
401Token has been revokedToken has been revoked
403Token lacks required scope: offer_import:writeScope mismatch
405Method not allowedOnly POST allowed
500Organisation owner not foundOrg configuration issue — contact support
500Failed to create offer: ...Database error

All error responses follow this schema:

{
"success": false,
"error": "<plain-text description>"
}
  1. Validate the token — SHA-256 lookup, scope check, revoke check.
  2. Check idempotency — does the offer already exist? If so, return the existing one.
  3. Create or reuse the company — if company_name is set, Flino looks for an existing company with the same name in your org. If none, a new one is created.
  4. Create or reuse the contact — using contact_email (when set) as the key; otherwise by name. Flino splits contact_name into first and last name automatically.
  5. Create the offer as draft — with send status not_sent, source zapier, your external_id and external_source for the next idempotency check.
  • The offer lands in the offer import and in the pipeline with status New.
  • You open it manually, fill in missing data (salutation, email address, description), and send it from Flino as usual.
  • No sequence is started, no email is sent — the webhook only creates the offer.

No hard rate limits documented at the moment. If you plan a bulk import (several hundred offers at once), spread the calls out and talk to support so your org doesn’t accidentally trip an anomaly check.

No Flino-specific rate limits at the moment. We reserve the right to throttle in case of abuse. Idempotency for retries is ensured via external_id + content hash — repeated calls with identical data don’t produce a duplicate offer.