API propform (REST)

Con l'API propform puoi gestire i tuoi moduli, gli invii e le statistiche da applicazioni esterne, ad esempio dai tuoi strumenti, script o piattaforme di automazione come Zapier, Make o n8n.

> URL di base: https://propform.io/api/v1 > > Tutte le richieste e le risposte sono in formato JSON (Content-Type: application/json). I timestamp sono in formato ISO-8601 (ad es. 2026-07-07T08:30:00+00:00).

Guida rapida in 3 passaggi

  1. Creare una chiave API: Vai su Impostazioni → Chiavi API, clicca su Crea chiave API e copia la chiave visualizzata (formato pf_live_…). Per motivi di sicurezza, ti verrà mostrata solo questa volta.

  2. Verifica la connessione: sostituisci pf_live_DEIN_KEY e accedi a:

    curl https://propform.io/api/v1/me \
      -H "Authorization: Bearer pf_live_DEIN_KEY"
    

    Risposta:

    { "data": { "id": 42, "name": "Max Makler", "email": "max@makler.de" } }
    
  3. Prima richiesta effettiva – elenca i tuoi moduli:

   curl https://propform.io/api/v1/forms \
     -H "Authorization: Bearer pf_live_DEIN_KEY"

Autenticazione

L’API utilizza le chiavi API come token bearer. Ogni richiesta richiede la chiave nell’intestazione Authorization (Authorization: Bearer pf_live_…). In assenza di chiave o in caso di chiave non valida, l’API risponde con 401.

  • Puoi gestire le chiavi in Impostazioni → Chiavi API: crearle, assegnare loro un nome e revocarle in qualsiasi momento.
  • Una chiave revocata perde immediatamente l’accesso.
  • Per ogni account sono consentite al massimo 10 chiavi API.
  • Tratta la chiave come una password: essa consente l’accesso completo ai tuoi moduli e ai tuoi invii. Non rivelarla nel frontend del browser né in repository pubblici.

Limiti di frequenza

  • 60 richieste al minuto per chiave.
  • Inoltre, 300 richieste al minuto per indirizzo IP (protezione dal sovraccarico).

In caso di superamento del limite, l’API risponde con 429 Too Many Requests e un’intestazione Retry-After (secondi che mancano al prossimo tentativo consentito). Inserisci una breve pausa nelle automazioni quando visualizzi questo stato.

Formato della risposta, impaginazione ed errori

Le risposte andate a buon fine forniscono i dati nel campo data.

Gli elenchi sono impaginati. Gestiscili tramite per_page (impostazione predefinita 25, max. 100) e page. La risposta contiene inoltre links e meta:

{
  "data": [ { "id": 14, "internal_title": "Kontaktformular", "active": true } ],
  "links": { "first": "...", "last": "...", "prev": null, "next": "...?page=2" },
  "meta": { "current_page": 1, "per_page": 25, "total": 63, "last_page": 3 }
}

Gli errori hanno sempre un campo message; in caso di errori di convalida (422), i dettagli per ciascun campo sono riportati in errors:

{
  "message": "The url field is required.",
  "errors": { "url": ["The url field is required."] }
}
Stato Significato
200 / 201 Operazione riuscita (201 = nuovo record creato)
204 Operazione riuscita, nessun dato di risposta (ad es. cancellazione)
401 Chiave API assente o non valida
403 Il record non appartiene al tuo account (o funzione non inclusa nel piano tariffario)
404 Il record non esiste
409 Azione attualmente non possibile (ad es. onOffice non connesso)
422 Errore di convalida – Dettagli nel campo errors
429 Limite di rate raggiunto – Vedi Retry-After

Moduli

Metodo Endpoint Descrizione
GET /forms Elenca i propri moduli. Filtri: active (0/1), updated_since (data), per_page, page
POST /forms Crea un nuovo modulo – è necessario solo internal_title, si avvia inattivo
GET /forms/{id} Leggi il modulo per intero (compresi i campi, l’e-mail e la configurazione di ChatGPT)
PATCH /forms/{id} Configurare il modulo – vengono modificati solo i campi inviati
POST /forms/{id}/copy Copia del modulo (compresi campi, regole, configurazione e-mail); la copia si avvia inattiva

Il modulo è una risorsa nell’API: puoi leggere e modificare le impostazioni relative all’invio delle e-mail e all’integrazione con ChatGPT direttamente sul modulo. I nomi dei campi corrispondono esattamente alle impostazioni nell’editor del modulo.

curl -X PATCH https://propform.io/api/v1/forms/123 \
  -H "Authorization: Bearer pf_live_DEIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Neuer Titel", "active": true}'

Caratteristiche importanti:

  • Semantica PATCH: i campi che non invii rimangono invariati. Puoi quindi modificare singole impostazioni in tutta sicurezza.
  • Attivare ("active": true) funziona solo finché non viene raggiunto il limite del tuo piano relativo ai moduli attivi (altrimenti 422). La disattivazione è sempre possibile.
  • I moduli non possono essere eliminati tramite API: questa operazione è possibile solo nella dashboard, per scelta.
  • Non sono modificabili, tra gli altri, slug, i contatori e i flag dei modelli. Le password (ad es. la password del modulo) sono modificabili, ma non compaiono mai nelle risposte.
  • Ogni risposta al modulo contiene l’url pubblico del modulo.

Impostazioni dei moduli utilizzate di frequente

Un modulo dispone di oltre 100 impostazioni (tutte quelle offerte anche dall’editor). Le più importanti:

Campo Significato
internal_title Nome interno (visibile solo nella dashboard)
title Titolo pubblico del modulo
custom_slug Parte descrittiva dell’URL (deve essere univoca nel tuo account)
active Modulo online (true) o offline (false)
description Testo introduttivo sopra il modulo
submit_button_label Etichetta del pulsante di invio
thankyou_headline / thankyou_text Titolo e testo della pagina di ringraziamento
redirect URL di reindirizzamento dopo l’invio (al posto della pagina di ringraziamento)
background_color / accent_color Colori di design (esadecimali, ad es. #0d6efd)
subject / body / receiver Oggetto, testo e destinatario dell’e-mail di conferma
send_email_via_onoffice Invia l’e-mail di conferma tramite onOffice

Puoi visualizzare l’oggetto completo in qualsiasi momento tramite GET /forms/{id}.

👉 Trovi l'elenco completo di tutti i campi e formati nella referenza dei campi API.

Campi

Metodo Endpoint Descrizione
GET /forms/{id}/fields Campi di un modulo (ordinati per posizione)
POST /forms/{id}/fields Crea campo – onoffice_module è obbligatorio, la posizione viene aggiunta automaticamente alla fine
GET /fields/{id} Leggere un singolo campo
PATCH /fields/{id} Modificare un campo (solo proprietà inviate)
DELETE /fields/{id} Elimina campo
PATCH /forms/{id}/fields/reorder Riordinare i campi – field_ids deve contenere tutti gli ID dei campi del modulo nell'ordine desiderato
curl -X POST https://propform.io/api/v1/forms/123/fields \
  -H "Authorization: Bearer pf_live_DEIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"onoffice_module": "address", "onoffice_field_name": "Vorname", "label": "Vorname", "required": true}'

Proprietà dei campi di uso frequente

Campo Significato
onoffice_module Modulo di destinazione: address, estate, task, calendar, agentsLog (obbligatorio al momento della creazione)
onoffice_field_name Nome del campo di destinazione in onOffice (vedi Configurazione dei campi onOffice)
label Etichetta del campo nel modulo
hint / placeholder Testo di aiuto o segnaposto nel campo di immissione
required Campo obbligatorio (true/false)
hidden / disabled / read_only Nascosto / disattivato / di sola lettura
default_value Valore predefinito
half_size Visualizza il campo a mezza larghezza sugli schermi di grandi dimensioni
datalist Valori di selezione come elenco separato da punti e virgola (Rot;Grün;Blau)

Ordinamento: nel caso di reorder, l’ordine nell’array field_ids determina la nuova posizione. L'array deve contenere esattamente tutti gli ID dei campi del modulo (se ne manca uno o ne è presente uno estraneo → 422):

curl -X PATCH https://propform.io/api/v1/forms/123/fields/reorder \
  -H "Authorization: Bearer pf_live_DEIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"field_ids": [4712, 4711, 4713]}'

Configurazione dei campi di onOffice

GET /onoffice/fields fornisce la configurazione dei campi del tuo account onOffice – per ogni modulo (address, estate, task, calendar, agentsLog, file, project) tutti i campi con etichetta, tipo, lunghezza e valori consentiti. In questo modo puoi trovare i valori validi per onoffice_field_name. Con ?module=address puoi filtrare per un modulo.

{
  "data": {
    "address": {
      "Vorname":  { "label": "Vorname", "type": "varchar", "length": 50, "default": null, "permittedvalues": null },
      "Anrede":   { "label": "Anrede",  "type": "singleselect", "length": null, "default": null,
                    "permittedvalues": { "HERR": "Herr", "FRAU": "Frau" } }
    }
  }
}

> È necessario che la connessione all’API di onOffice funzioni correttamente; in caso contrario, l’endpoint risponde con 409.

Condizioni (regole)

Metodo Endpoint Descrizione
GET /forms/{id}/rules Regole di un modulo (ordinate per priorità)
PUT /forms/{id}/rules Sostituisci tutte le regole del modulo (array vuoto = elimina tutte)

L'endpoint di scrittura funziona come l'editor delle condizioni nella dashboard: tutte le regole vengono sempre salvate in una volta sola (non è possibile aggiungerne una singola alla volta). Quindi, leggi prima con GET, modifica l’elenco e invialo per intero tramite PUT. Una regola è costituita da clauses (condizioni «Se», collegate a mode = all/any) e actions (azioni "allora"):

curl -X PUT https://propform.io/api/v1/forms/123/rules \
  -H "Authorization: Bearer pf_live_DEIN_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rules": [{
    "title": "Zusatzfeld einblenden",
    "mode": "all", "priority": 10, "is_enabled": 1, "run_on_init": 1,
    "clauses": [{"when_field_id": "4711", "operator": "equals", "value": "Ja"}],
    "actions": [{"order": 10, "action": "show", "target_field_id": "4712"}]
  }]}'
  • Operatori: equals, not_equals, empty, not_empty, starts_with, not_starts_with, contains, not_contains
  • Azioni: show, hide, enable, disable, require, optional, hide_option, unhide_option, set_value, set_label, make_readonly, unmake_readonly, enable_rules_by_tag, disable_rules_by_tag
  • Come destinazione sono consentiti, oltre agli ID dei campi, anche destinazioni virtuali: __form_title, __form_description, __submit, __submit_spinner, __multistep_back, __multistep_next (in questo caso solo set_label, show, hide; per lo spinner solo set_label).
  • Tutti gli ID dei campi a cui si fa riferimento devono appartenere al modulo.
  • Limiti per richiesta: max. 200 regole, 50 clausole/azioni ciascuna.
  • Le condizioni sono disponibili, come nella dashboard, solo a partire dal piano con 10 moduli attivi (altrimenti 403).

Per i dettagli sul funzionamento delle regole, consulta Condizioni, regole e calcoli.

Invii e statistiche

Metodo Endpoint Descrizione
GET /forms/{id}/submissions Invii di un modulo. Filtro: created_after, created_before, sort (asc/desc), per_page
GET /submissions/{id} Invio singolo
GET /forms/{id}/stats Visualizzazioni, invii e tasso di conversione; opzionale from/to (data)

Il contenuto di un invio (content) è raggruppato in base ai moduli di onOffice:

{
  "data": {
    "id": 456, "form_id": 123, "index": 42,
    "content": { "address": { "Vorname": "Max", "Name": "Mustermann" }, "estate": { "Id": "131" } },
    "ip_address": "203.0.113.7",
    "created_at": "2026-07-07T08:30:00+00:00"
  }
}

Se nelle impostazioni sulla privacy hai disattivato il salvataggio dei dati degli invii, content è sempre null – l’invio viene comunque conteggiato nelle statistiche. La risposta stats contiene views, submissions e conversion_rate.

Webhook (nuovi invii in tempo reale)

Invece di interrogare regolarmente gli invii, puoi ricevere notifiche tramite webhook: propform invia immediatamente un POST al tuo URL ogni volta che viene effettuato un nuovo invio.

💡 Puoi gestire i webhook anche nel frontend, senza chiave API: nelle impostazioni alla voce «Webhooks» (per tutti i moduli) oppure direttamente nell'editor dei moduli alla voce «Webhook (Zapier, n8n, Make …)».

Metodo Endpoint Descrizione
GET /webhooks Elenca i tuoi abbonamenti ai webhook
POST /webhooks Crea un abbonamento: url (obbligatorio), form_id (facoltativo – se non specificato, l’abbonamento si applica a tutti i tuoi moduli), payload_template (facoltativo – vedi sotto), message_key (facoltativo – nome del campo del messaggio risolto, predefinito text)
DELETE /webhooks/{id} Elimina abbonamento

Requisiti per l’URL: deve essere accessibile pubblicamente e utilizzare http:// o https://. Gli indirizzi interni o locali (ad es. localhost, 127.0.0.1, IP di reti private) vengono rifiutati per motivi di sicurezza (422). Poiché i dati inviati vengono trasmessi, utilizza se possibile https://.

La risposta generata al momento della creazione contiene l’secret, con cui viene firmata ogni consegna (puoi recuperarlo in qualsiasi momento tramite GET /webhooks):

{
  "data": {
    "id": 7, "event": "submission.created",
    "url": "https://example.com/hook", "form_id": 123,
    "secret": "a1b2c3…", "payload_template": null, "message_key": "text", "is_active": true,
    "failure_count": 0, "last_success_at": null, "last_failure_at": null,
    "created_at": "2026-07-07T08:30:00+00:00"
  }
}

Ogni consegna contiene due intestazioni:

  • X-Propform-Event – il tipo di evento (attualmente sempre submission.created)
  • X-Propform-Signaturesha256=<HMAC-SHA256 des Request-Bodys mit deinem secret>

Esempio di payload:

{
  "event": "submission.created",
  "created_at": "2026-07-07T08:30:00+00:00",
  "form": { "id": 123, "slug": "aB3x…", "custom_slug": "kontakt", "title": "…", "internal_title": "…" },
  "submission": { "id": 456, "index": 42, "content": { "address": { "Vorname": "Max" } } },
  "onoffice_ids": { "address": { "ID": "789" } }
}

Testo libero / messaggio con macro onOffice (payload_template)

Facoltativamente, puoi salvare un modello di testo libero per ogni abbonamento. Dopo ogni invio del modulo, propform vi risolve le macro onOffice (sui record creati o modificati dall'invio) e include il risultato nel payload come campo di messaggio dedicato: ideale come messaggio pronto per Slack e simili, senza dover assemblare nulla nel flusso di lavoro ricevente. Il nome del campo è text per impostazione predefinita (vedi sotto).

Esempio di modello:

Nuova richiesta di _Vorname _Name per _objekttitel → _getEstateLink

nel payload diventa:

"text": "Nuova richiesta di Max Mustermann per Traumwohnung am Stadtpark → https://smart.onoffice.de/…"

Nome del campo del messaggio (message_key): Per impostazione predefinita il campo si chiama text: così Slack, Microsoft Teams e Google Chat interpretano il payload direttamente come messaggio. Discord si aspetta content. Per Zapier/n8n/Make il nome è libero. Puoi modificarlo per ogni webhook tramite il parametro message_key (nel frontend alla voce «Nome del campo del messaggio»).

Da tenere presente:

  • Funzionano le stesse macro dei modelli e-mail di propform (macro dei campi onOffice, _getAddressLink, _getEstateLink, …).
  • Una macro può essere risolta solo se l'invio dispone del contesto di record corrispondente (ad es. _objekttitel richiede un immobile collegato). Senza contesto, la macro resta non risolta nel testo.
  • I valori scritti solo dopo l'invio del webhook (caricamenti di file su onOffice, campi a selezione multipla scritti tramite «estendi») possono ancora mancare nel testo risolto.
  • Senza modello di testo libero non viene inviato alcun campo messaggio.
  • Massimo 5.000 caratteri.

Verifica della firma (consigliata)

Verifica la firma per assicurarti che la richiesta provenga effettivamente da propform. Calcola l’HMAC sul corpo della richiesta grezzo (non sul JSON analizzato) e confrontalo in tempo reale.

PHP:

$secret    = 'DEIN_WEBHOOK_SECRET';
$body      = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PROPFORM_SIGNATURE'] ?? '';
$expected  = 'sha256=' . hash_hmac('sha256', $body, $secret);

if (!hash_equals($expected, $signature)) {
    http_response_code(403);
    exit('Ungültige Signatur');
}
// ab hier: $body ist echt – json_decode($body, true) verarbeiten

Node.js (Express):

const crypto = require('crypto');

// WICHTIG: express.raw() nutzen, damit der Body unverändert bleibt
app.post('/hook', express.raw({ type: 'application/json' }), (req, res) => {
  const secret   = process.env.PROPFORM_WEBHOOK_SECRET;
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(req.body).digest('hex');
  const got      = req.header('X-Propform-Signature') || '';

  if (expected.length !== got.length ||
      !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got))) {
    return res.status(403).send('Ungültige Signatur');
  }
  const payload = JSON.parse(req.body.toString());
  res.sendStatus(200);
});

Consegna, ripetizione e limiti

  • Il tuo endpoint dovrebbe rispondere con uno stato 2xx. In caso di errori, propform tenta la consegna fino a 3 volte (con un intervallo di attesa).
  • Se un abbonamento fallisce 20 volte consecutive, viene disattivato automaticamente (is_active: false, visibile tramite GET /webhooks). A quel punto basta ricrearlo.
  • Per ogni account sono consentiti al massimo 20 abbonamenti webhook.
  • Gli allegati non fanno parte del payload.

Gruppi di moduli

GET /form-groups elenca i tuoi gruppi di moduli con i relativi ID. Puoi assegnare gli ID nel PATCH del modulo come form_groups (array).

Suggerimenti per Zapier, Make & Co.

  • Trigger «Nuovo invio»: crea un abbonamento al webhook (vedi sopra) – più affidabile e veloce rispetto alle interrogazioni periodiche.
  • Alternativa al polling: prendere nota di GET /forms/{id}/submissions?sort=desc e del valore più alto già elaborato id.
  • Test di connessione: GET /me.
  • Riconoscimento di nuovi moduli: GET /forms?updated_since=….
  • In caso di 429 inserire una breve attesa e rispettare l’intestazione Retry-After.

Gestione delle versioni

La versione attuale è v1 ed è fissata nel percorso (/api/v1). Aggiungiamo nuovi campi ed endpoint in modo retrocompatibile senza modificare la versione; pertanto, il tuo client dovrebbe semplicemente ignorare i campi sconosciuti presenti nelle risposte. Modifiche fondamentali e non retrocompatibili verrebbero pubblicate con una nuova versione (/api/v2).