Con la API de propform puedes gestionar tus formularios, envíos y estadísticas desde aplicaciones externas, por ejemplo, desde tus propias herramientas, scripts o plataformas de automatización como Zapier, Make o n8n.
> URL base: https://propform.io/api/v1
>
> Todas las solicitudes y respuestas están en formato JSON (Content-Type: application/json). Las marcas de tiempo están en formato ISO-8601 (p. ej., 2026-07-07T08:30:00+00:00).
Crear una clave API: Ve a Configuración → Claves API, haz clic en Crear clave API y copia la clave que se muestra (formato pf_live_…). Por motivos de seguridad, solo se te mostrará esta única vez.
Probar la conexión: Sustituye pf_live_DEIN_KEY y accede a:
curl https://propform.io/api/v1/me \
-H "Authorization: Bearer pf_live_DEIN_KEY"
Respuesta:
{ "data": { "id": 42, "name": "Max Makler", "email": "max@makler.de" } }
Primera consulta real: listar tus formularios:
curl https://propform.io/api/v1/forms \
-H "Authorization: Bearer pf_live_DEIN_KEY"
La API utiliza claves API como tokens de portador. Cada solicitud debe incluir la clave en el encabezado Authorization (Authorization: Bearer pf_live_…). Si no se incluye la clave o esta no es válida, la API responde con 401.
Si se supera este límite, la API responde con 429 Too Many Requests y un encabezado Retry-After (segundos hasta el siguiente intento permitido). Incorpora una breve espera en las automatizaciones cuando veas este estado.
Las respuestas correctas proporcionan los datos en el campo data.
Las listas están paginadas. Contrólalas mediante per_page (por defecto 25, máx. 100) y page. La respuesta incluye además links y 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 }
}
Los errores siempre tienen un campo message; en el caso de los errores de validación (422), los detalles de cada campo se encuentran en errors:
{
"message": "The url field is required.",
"errors": { "url": ["The url field is required."] }
}
| Estado | Significado |
|---|---|
200 / 201 |
Éxito (201 = recién creado) |
204 |
Éxito, sin datos de respuesta (p. ej., eliminación) |
401 |
Clave API inexistente o no válida |
403 |
El registro no pertenece a tu cuenta (o la función no está incluida en el plan) |
404 |
El registro no existe |
409 |
Acción no disponible actualmente (p. ej., onOffice no está conectado) |
422 |
Error de validación: detalles en el campo errors |
429 |
Límite de rate alcanzado: ten en cuenta Retry-After |
| Método | Punto final | Descripción |
|---|---|---|
GET |
/forms |
Mostrar una lista de formularios propios. Filtro: active (0/1), updated_since (fecha), per_page, page |
POST |
/forms |
Crear un nuevo formulario: solo se necesita internal_title; se inicia inactivo |
GET |
/forms/{id} |
Leer el formulario completo (incluidos los campos, la configuración del correo electrónico y de ChatGPT) |
PATCH |
/forms/{id} |
Configurar formulario: solo se modifican los campos enviados |
POST |
/forms/{id}/copy |
Copiar el formulario (incluidos los campos, las reglas y la configuración del correo electrónico); la copia se inicia inactiva |
El formulario es un recurso en la API: también puedes leer y escribir los ajustes de envío de correo electrónico y de la integración con ChatGPT directamente en el formulario. Los nombres de los campos coinciden exactamente con los ajustes del editor de formularios.
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}'
Características importantes:
"active": true) solo funciona mientras no se haya alcanzado el límite de formularios activos de tu plan (en caso contrario, 422). Desactivar siempre es posible.slug, los contadores y los indicadores de plantilla. Las contraseñas (por ejemplo, la contraseña del formulario) son modificables, pero nunca aparecen en las respuestas.url público del formulario.Un formulario tiene más de 100 configuraciones (todas las que ofrece también el editor). Las más importantes:
| Campo | Significado |
|---|---|
internal_title |
Nombre interno (visible solo en el panel de control) |
title |
Título público del formulario |
custom_slug |
Parte descriptiva de la URL (debe ser única en tu cuenta) |
active |
Formulario en línea (true) o fuera de línea (false) |
description |
Texto introductorio encima del formulario |
submit_button_label |
Texto del botón de envío |
thankyou_headline / thankyou_text |
Título y texto de la página de agradecimiento |
redirect |
URL de redireccionamiento tras el envío (en lugar de la página de agradecimiento) |
background_color / accent_color |
Colores de diseño (hexadecimal, p. ej., #0d6efd) |
subject / body / receiver |
Asunto, texto y destinatario del correo electrónico de confirmación |
send_email_via_onoffice |
Enviar el correo electrónico de confirmación a través de onOffice |
Puedes ver el objeto completo en cualquier momento a través de GET /forms/{id}.
👉 Encontrarás la lista completa de todos los campos y formatos en la referencia de campos de la API.
| Método | Punto final | Descripción |
|---|---|---|
GET |
/forms/{id}/fields |
Campos de un formulario (ordenados por posición) |
POST |
/forms/{id}/fields |
Crear campo – onoffice_module es obligatorio; la posición se coloca automáticamente al final |
GET |
/fields/{id} |
Leer un campo concreto |
PATCH |
/fields/{id} |
Modificar campo (solo propiedades enviadas) |
DELETE |
/fields/{id} |
Eliminar campo |
PATCH |
/forms/{id}/fields/reorder |
Reordenar campos: field_ids debe contener todos los ID de campo del formulario en el orden deseado |
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}'
| Campo | Significado |
|---|---|
onoffice_module |
Módulo de destino: address, estate, task, calendar, agentsLog (obligatorio al crear el campo) |
onoffice_field_name |
Nombre del campo de destino en onOffice (véase Configuración de campos de onOffice) |
label |
Etiqueta del campo en el formulario |
hint / placeholder |
Texto de ayuda o marcador de posición en el campo de entrada |
required |
Campo obligatorio (true/false) |
hidden / disabled / read_only |
Oculto / desactivado / solo de lectura |
default_value |
Valor predefinido |
half_size |
Mostrar el campo a medio ancho en pantallas grandes |
datalist |
Valores de selección como lista separada por punto y coma (Rot;Grün;Blau) |
Ordenación: En el caso de reorder, el orden en la matriz field_ids determina la nueva posición. La matriz debe contener exactamente todos los ID de campo del formulario (si falta alguno o hay alguno que no pertenezca al formulario → 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]}'
GET /onoffice/fields proporciona la configuración de campos de tu cuenta de onOffice, por módulo (address, estate, task, calendar, agentsLog, file, project) todos los campos con su etiqueta, tipo, longitud y valores permitidos. Así encontrarás los valores válidos para onoffice_field_name. Con ?module=address puedes filtrar por un módulo.
{
"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" } }
}
}
}
> Es necesario disponer de una conexión a la API de onOffice que funcione correctamente; de lo contrario, el punto final responderá con 409.
| Método | Punto final | Descripción |
|---|---|---|
GET |
/forms/{id}/rules |
Reglas de un formulario (ordenadas por prioridad) |
PUT |
/forms/{id}/rules |
Reemplazar todas las reglas del formulario (matriz vacía = borrar todas) |
El punto final de escritura funciona igual que el editor de condiciones del panel de control: Siempre se guardan todas las reglas de una sola vez (no se añaden de forma individual). Por lo tanto, primero lee con GET, modifica la lista y devuélvela completa mediante PUT. Una regla se compone de clauses (condiciones «Si», vinculadas a mode = all/any) y actions (acciones «entonces»):
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"}]
}]}'
equals, not_equals, empty, not_empty, starts_with, not_starts_with, contains, not_containsshow, 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__form_title, __form_description, __submit, __submit_spinner, __multistep_back, __multistep_next (en este caso, solo set_label, show, hide; en el spinner, solo set_label).403).Encontrarás más detalles sobre el funcionamiento de las reglas en Condiciones, reglas y cálculos.
| Método | Punto final | Descripción |
|---|---|---|
GET |
/forms/{id}/submissions |
Envíos de un formulario. Filtros: created_after, created_before, sort (asc/desc), per_page |
GET |
/submissions/{id} |
Envío individual |
GET |
/forms/{id}/stats |
Visitas, envíos y tasa de conversión; opcionalmente from/to (fecha) |
El contenido de un envío (content) se agrupa por módulos de 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"
}
}
Si has desactivado el almacenamiento de datos de envío en la configuración de privacidad, content siempre será null, pero el envío seguirá contando para las estadísticas. La respuesta stats contiene views, submissions y conversion_rate.
En lugar de consultar las respuestas periódicamente, puedes recibir notificaciones mediante webhooks: propform envía inmediatamente un POST a tu URL cada vez que se recibe una nueva respuesta.
💡 También puedes gestionar los webhooks en el frontend sin clave API: en la configuración, en «Webhooks» (para todos los formularios) o directamente en el editor de formularios, en «Webhooks (Zapier, n8n, Make …)».
| Método | Punto final | Descripción |
|---|---|---|
GET |
/webhooks |
Mostrar tus suscripciones a webhooks |
POST |
/webhooks |
Crear suscripción: url (obligatorio), form_id (opcional; si no se especifica, la suscripción se aplica a todos tus formularios), payload_template (opcional; véase más abajo), message_key (opcional; nombre del campo del mensaje resuelto, por defecto text) |
DELETE |
/webhooks/{id} |
Eliminar suscripción |
Requisitos de la URL: debe ser de acceso público y utilizar http:// o https://. Las direcciones internas o locales (p. ej., localhost, 127.0.0.1, direcciones IP de redes privadas) se rechazan por motivos de seguridad (422). Dado que se transmiten los datos de envío, utiliza, en la medida de lo posible, https://.
La respuesta al crear el evento contiene el secret, con el que se firma cada entrega (puedes recuperarlo más tarde en cualquier momento mediante 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"
}
}
Cada entrega contiene dos encabezados:
X-Propform-Event: el tipo de evento (actualmente siempre es submission.created)X-Propform-Signature – sha256=<HMAC-SHA256 des Request-Bodys mit deinem secret>Ejemplo de carga útil:
{
"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" } }
}
payload_template)Opcionalmente, puedes guardar una plantilla de texto libre para cada suscripción. Tras cada envío del formulario, propform resuelve en ella las macros de onOffice (con los registros que el envío ha creado o editado) e incluye el resultado como un campo de mensaje propio en la carga útil: ideal como mensaje listo para Slack y similares, sin tener que montar nada en el flujo de trabajo receptor. El nombre del campo es text por defecto (véase más abajo).
Ejemplo de plantilla:
Nueva solicitud de _Vorname _Name sobre _objekttitel → _getEstateLink
se convierte en la carga útil en:
"text": "Nueva solicitud de Max Mustermann sobre Traumwohnung am Stadtpark → https://smart.onoffice.de/…"
Nombre del campo del mensaje (message_key): Por defecto el campo se llama text, así Slack, Microsoft Teams y Google Chat interpretan la carga útil directamente como mensaje. Discord espera content. Para Zapier/n8n/Make el nombre es libre. Puedes cambiarlo por webhook mediante el parámetro message_key (en el frontend, en «Nombre del campo del mensaje»).
Ten en cuenta:
_getAddressLink, _getEstateLink, …)._objekttitel necesita un inmueble vinculado). Sin contexto, la macro permanece sin resolver en el texto.Comprueba la firma para asegurarte de que la solicitud procede realmente de propform. Calcula el HMAC a partir del cuerpo de la solicitud en bruto (no a partir del JSON analizado) y compáralo en tiempo real.
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);
});
2xx. En caso de error, propform intentará la entrega hasta 3 veces (con un tiempo de espera).is_active: false, visible a través de GET /webhooks). A continuación, solo tienes que volver a crearla.GET /form-groups muestra una lista de tus grupos de formularios con sus ID. Puedes asignar los ID en el PATCH del formulario como form_groups (matriz).
GET /forms/{id}/submissions?sort=desc y del id más alto ya procesado.GET /me.GET /forms?updated_since=….429, incorporar una breve espera y respetar el encabezado Retry-After.La versión actual es v1 y está fijada en la ruta (/api/v1). Añadimos nuevos campos y puntos finales de forma compatible con versiones anteriores sin cambiar la versión; por lo tanto, tu cliente debería simplemente ignorar los campos desconocidos en las respuestas. Los cambios fundamentales que no sean compatibles con versiones anteriores aparecerían bajo una nueva versión (/api/v2).