Integraciones
Crea y gestiona conexiones con bases de datos, servicios de envío de correo, CRM y Google Drive, y úsalas para consultar, enviar, buscar y escribir: son las mismas conexiones que usan el chat, los agentes y los flujos de trabajo.
Las integraciones permiten que StickyPrompts actúe sobre tus propios sistemas. Con la API puedes crear conexiones y usarlas directamente: ejecutar SQL en una base de datos, enviar un correo electrónico, buscar o actualizar datos en un CRM y mover archivos desde y hacia Google Drive. Todo lo que se describe aquí es el mismo mecanismo que usa la aplicación cuando alguien lo pide en el chat.
Todas las rutas son relativas a https://api.stickyprompts.com/api/v1. La clave actúa en nombre de su propietario: se aplican las mismas reglas de compartición y de administración que en la aplicación.
Conceptos
Proveedores
:provider en las rutas y provider en las respuestas usan exactamente estas cadenas:
| Proveedor | Familia | Cómo se conecta | Conexiones |
|---|---|---|---|
mysql, mariadb, postgres, sqlserver | base de datos | credenciales | ilimitadas |
smtp, postmark, resend, sendgrid | correo electrónico | credenciales | una sola conexión de correo electrónico en total por propietario, sea cual sea el proveedor |
pipedrive | CRM | credenciales | 1 |
activecampaign | CRM | credenciales | 1 |
brevo | CRM | credenciales | 1 |
google_drive | almacenamiento | OAuth en un navegador | 1, solo personal |
Los límites se cuentan por propietario: por usuario en las conexiones personales y por espacio de trabajo en las compartidas.
Ámbito
Cada conexión es personal (solo su propietario la ve y la usa) o workspace (todos los miembros del espacio de trabajo pueden usarla). Solo los propietarios y administradores del espacio de trabajo pueden crear, modificar o eliminar una conexión workspace, o leer sus datos guardados; cualquier miembro puede usarla: consultarla, enviar a través de ella, leer registros del CRM y, cuando la conexión tiene acceso de escritura, escribirlos. Una conexión que no puedes ver responde 404, nunca 403.
Credenciales frente a OAuth
- Las conexiones de bases de datos, correo electrónico y CRM se crean en una sola llamada con las credenciales en el cuerpo. Antes, el servidor las verifica con el proveedor: abre la conexión con la base de datos, se autentica en SMTP o envía un mensaje de prueba a través de Postmark, Resend o SendGrid. Un cliente que solo use la API puede hacer todo el proceso de principio a fin.
- Google Drive usa OAuth. La llamada de conexión devuelve una
auth_url; una persona debe abrirla y aceptar la pantalla de consentimiento de Google. Tu código puede pasar el enlace a un usuario y consultar periódicamenteGET /integration/connections?provider=google_drivehasta que aparezca la conexión.
Ningún endpoint devuelve nunca los secretos.
Estado
El status de una conexión es active, needs_reauth, revoked o error. Cualquier ruta de conexión que se llame sobre una conexión no activa responde 409 {"error":"reconnect_required"}. Actualmente, solo las conexiones de Google Drive pueden dejar de estar en active; repáralas con el endpoint reconnect.
Errores
| Estado | Ejemplo de cuerpo | Significado |
|---|---|---|
400 | "invalid request", "query is required" | Entrada mal formada. |
403 | "you do not have permission to manage this connection" | Es una conexión del espacio de trabajo y no eres propietario ni administrador. |
403 | "this connection is read-only; enable write access on the connection to create or edit records" | Escritura en el CRM sin acceso de escritura. |
404 | "integration is not connected or does not exist" | Conexión desconocida o que no es tuya. |
409 | "reconnect_required", "connection limit reached for this integration" | Hay que volver a autorizarla, o se ha superado el límite. |
413 | "the attachments are too large to send" | |
422 | "failed to connect to the integration with the given credentials: ..." | Credenciales rechazadas al conectar o al actualizar. |
422 | "query is not a single read-only select statement" | SQL no permitido con el nivel de permiso de la conexión. |
422 | "the message was rejected: ..." | El proveedor de correo electrónico rechazó el mensaje. |
501 | "integration provider does not support this capability: ..." | Por ejemplo, tratos en un CRM que no tiene tratos. |
502 | "failed to send the message: ...", "failed to reach the crm provider: ..." | Ha fallado el proveedor externo. |
504 | "query timed out" | La consulta a la base de datos superó su tiempo de espera. |
500 | "internal server error" | Errores no mapeados, incluidos los errores de SQL que genera la propia base de datos. |
Catálogo y conexiones
GET /integration
/api/v1/integration El catálogo de proveedores, con lo que puede hacer cada uno y cuántas conexiones tienes ya.
[
{
"provider": "postgres",
"display_name": "PostgreSQL",
"capabilities": ["test_connection", "schema_browse", "query"],
"availability": "available",
"connection_count": 1,
"max_connections": 0
}
]
max_connections: 0 significa ilimitadas. El límite compartido de una sola conexión de correo electrónico no se refleja aquí.
GET /integration/ssh-tunnel/public-key
/api/v1/integration/ssh-tunnel/public-key Las conexiones de bases de datos pueden llegar a una base de datos privada a través de un host de salto SSH. Añade esta clave pública al authorized_keys del usuario SSH de tu host de salto y permite la dirección de StickyPrompts en tu firewall.
{ "public_key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... stickyprompts" }
POST /integration/:provider/connect
/api/v1/integration/:provider/connect Crea una conexión nueva (nunca sustituye una existente). El cuerpo es un único objeto plano: los campos comunes más los propios del proveedor.
-
namestring obligatorio - Un nombre que la gente reconozca, por ejemplo «Northwind Retail DB».
-
descriptionstring opcional - Opcional.
-
scopestring obligatorio personaloworkspace.workspacerequiere un propietario o administrador del espacio de trabajo.-
database_permissionstring opcional por defectoread_only - Solo para bases de datos:
read_only,create_updateofull_access. Consulta los niveles de permiso.
-
hoststring obligatorio - Nombre del host tal como lo ve StickyPrompts o, si usas SSH, tal como lo ve el host de salto.
-
portinteger opcional - De forma predeterminada, 3306 (MySQL, MariaDB), 5432 (PostgreSQL) o 1433 (SQL Server).
-
databasestring obligatorio -
schemastring opcional - De forma predeterminada,
publicen PostgreSQL ydboen SQL Server. -
usernamestring obligatorio - Usa un usuario dedicado con los privilegios mínimos que necesite la tarea.
-
passwordstring obligatorio -
ssl_modestring opcional - PostgreSQL:
disable,require(predeterminado),verify-ca,verify-full. MySQL/MariaDB:true,false,skip-verify,preferred(predeterminado). SQL Server:true(predeterminado),skip-verify,false. -
ssh_hoststring opcional - Enruta la conexión a través de este host de salto SSH.
-
ssh_portinteger opcional -
ssh_usernamestring opcional - Obligatorio con
ssh_host.
-
default_from_emailstring obligatorio - El remitente predeterminado. En Postmark, Resend y SendGrid debe ser ya un remitente que tu proveedor acepte, porque la prueba de conexión envía un mensaje de prueba real.
-
default_from_namestring opcional -
default_reply_tostring opcional -
server_tokenstring opcional - Postmark.
-
api_keystring opcional - Resend y SendGrid.
-
host, port, username, password, encryptionvarious opcional - SMTP.
encryptionesstarttls(predeterminado),sslonone;portes 587 de forma predeterminada, o 465 conssl.
-
api_tokenstring opcional - Pipedrive (obligatorio).
-
api_url, api_keystring opcional - ActiveCampaign (ambos obligatorios;
api_urles la URL de tu cuenta). -
api_keystring opcional - Brevo (obligatorio).
-
allow_writeboolean opcional por defectofalse - Sin este campo, la conexión es de solo lectura: añadir o editar contactos y crear tratos responde
403.
curl -X POST https://api.stickyprompts.com/api/v1/integration/postgres/connect \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Northwind Retail DB",
"scope": "workspace",
"database_permission": "read_only",
"host": "localhost",
"port": 5432,
"database": "retail",
"schema": "public",
"username": "sp_reader",
"password": "********",
"ssl_mode": "disable",
"ssh_host": "bastion.northwind.example",
"ssh_port": 22,
"ssh_username": "stickyprompts"
}'import os, requests
r = requests.post(
"https://api.stickyprompts.com/api/v1/integration/postmark/connect",
headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
json={
"name": "Northwind Mailer",
"scope": "workspace",
"default_from_email": "team@northwind.example",
"default_from_name": "Northwind Team",
"server_token": os.environ["POSTMARK_SERVER_TOKEN"],
},
)
print(r.status_code, r.json()) # 200 {"connection_id": 42}const res = await fetch("https://api.stickyprompts.com/api/v1/integration/pipedrive/connect", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STICKY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Pipedrive - Sales",
scope: "workspace",
api_token: process.env.PIPEDRIVE_TOKEN,
allow_write: true,
}),
});
console.log(res.status, await res.json()); // 200 {"connection_id": 43}Respuesta 200 {"connection_id": 42}. Con google_drive, la respuesta es 200 {"auth_url": "..."}: envía a una persona a esa URL para terminar.
GET /integration/connections
/api/v1/integration/connections Todas las conexiones que puedes ver (las tuyas y las compartidas de tu espacio de trabajo), de la más reciente a la más antigua. Opcionalmente, ?provider=<provider>.
[
{
"id": 42,
"provider": "postgres",
"name": "Northwind Retail DB",
"description": "Demo retail data: stores, orders, products, customers.",
"scope": "workspace",
"status": "active",
"account_name": "sp_reader@localhost:5432/retail",
"connected_at": "2026-09-28T12:55:00Z",
"database_permission": "read_only"
}
]
account_name es una etiqueta que no contiene secretos: user@host:port/database en las bases de datos, el remitente predeterminado en el correo electrónico y la URL de la cuenta en ActiveCampaign. El acceso de escritura del CRM no aparece en esta lista; consúltalo en GET .../crm/connection.
PATCH /integration/connections/:connectionId
/api/v1/integration/connections/:connectionId Cambia el nombre o el nivel de permiso de la base de datos, o rota las credenciales. Envía name siempre (el actual si no le cambias el nombre). description se escribe siempre, así que si lo omites, se borra. Solo cambian los campos del proveedor que envías; si hay algún campo de credenciales, la configuración combinada se vuelve a verificar antes de guardarla (422 si falla). En las bases de datos, "ssh_host": "" desactiva el túnel SSH. Devuelve 200 con el cuerpo vacío.
DELETE /integration/connections/:connectionId
/api/v1/integration/connections/:connectionId Elimina la conexión y sus credenciales guardadas. En Google Drive, primero se revoca la autorización en Google. Devuelve 204.
POST /integration/connections/:connectionId/reconnect
/api/v1/integration/connections/:connectionId/reconnect Inicia un nuevo consentimiento OAuth para una conexión de Google Drive y conserva su ID y su nombre. Devuelve 200 {"auth_url": "..."}. Los proveedores con credenciales responden 501; actualízalos con PATCH.
Bases de datos
Niveles de permiso
El database_permission de la conexión determina qué SQL acepta el endpoint de consulta, con independencia de lo que pueda hacer la clave de API:
| Nivel | Se permite |
|---|---|
read_only | Exactamente una sentencia SELECT o WITH. |
create_update | Exactamente una sentencia SELECT, WITH, INSERT o UPDATE. |
full_access | Todo lo que pueda ejecutar el usuario de la base de datos. Sin filtrado de sentencias. |
Con read_only y create_update se analiza toda la sentencia en busca de palabras clave prohibidas, así que se rechaza una consulta de solo lectura que simplemente mencione una palabra como update en el nombre de una columna o en una cadena. En MySQL, MariaDB y PostgreSQL, las sesiones de solo lectura también se abren en modo de solo lectura; SQL Server depende únicamente de la comprobación de palabras clave. En todos los casos, conéctate con un usuario de base de datos que solo tenga los permisos que quieras conceder.
GET /integration/connections/:connectionId/database/schema
/api/v1/integration/connections/:connectionId/database/schema Todas las tablas y vistas del esquema de la conexión, con sus columnas:
{
"schema": {
"name": "public",
"tables": [
{
"schema": "public",
"name": "demo_orders",
"columns": [
{ "name": "id", "data_type": "integer", "nullable": false },
{ "name": "customer_id", "data_type": "integer", "nullable": false },
{ "name": "status", "data_type": "text", "nullable": true }
]
}
]
}
}
GET /integration/connections/:connectionId/database/connection
/api/v1/integration/connections/:connectionId/database/connection Los datos de conexión guardados, sin la contraseña. En las conexiones del espacio de trabajo, solo para propietarios y administradores. Los campos que dejaste vacíos se devuelven como 0 o "", aunque se usara un valor predeterminado para conectar.
POST /integration/connections/:connectionId/database/query
/api/v1/integration/connections/:connectionId/database/query -
querystring obligatorio - Una sentencia SQL.
-
row_limitinteger opcional por defecto100 - Máximo 1000.
-
timeout_secondsinteger opcional por defecto10 - Máximo 30.
curl -X POST https://api.stickyprompts.com/api/v1/integration/connections/42/database/query \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "SELECT c.name, SUM(o.total) AS revenue FROM demo_orders o JOIN demo_customers c ON c.id = o.customer_id GROUP BY c.name ORDER BY revenue DESC LIMIT 5"}'import os, requests
r = requests.post(
"https://api.stickyprompts.com/api/v1/integration/connections/42/database/query",
headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
json={"query": "SELECT name, city FROM demo_customers ORDER BY name", "row_limit": 50},
)
data = r.json()
rows = [dict(zip(data["columns"], row)) for row in data["rows"]]const res = await fetch(
"https://api.stickyprompts.com/api/v1/integration/connections/42/database/query",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STICKY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "SELECT name, city FROM demo_customers ORDER BY name" }),
},
);
const { columns, rows, truncated } = await res.json();Respuesta a una sentencia SELECT:
{
"columns": ["name", "revenue"],
"rows": [["Hawthorne Goods", "18240.00"], ["Cedar & Pine Outfitters", "15110.50"]],
"row_count": 2,
"truncated": false
}
truncated: true significa que había más filas después de row_limit. Las columnas numéricas suelen llegar como cadenas. Las demás sentencias (permitidas en los niveles superiores) solo devuelven row_count, el número de filas afectadas. Los errores de SQL que genera la base de datos devuelven 500 sin el mensaje de la base de datos, así que prueba antes las consultas en un cliente de base de datos.
Correo electrónico
El correo siempre sale con las credenciales de la propia cuenta conectada; StickyPrompts no proporciona ningún remitente.
GET /integration/connections/:connectionId/email/connection
/api/v1/integration/connections/:connectionId/email/connection La configuración del remitente (provider, default_from_email, default_from_name, default_reply_to y, en SMTP, el host, el puerto, el usuario y el cifrado). Los tokens y las contraseñas nunca se devuelven. En las conexiones del espacio de trabajo, solo para propietarios y administradores.
POST /integration/connections/:connectionId/email/send
/api/v1/integration/connections/:connectionId/email/send -
toAddress[] obligatorio - Al menos un
{"email": "...", "name": "..."}. -
cc, bccAddress[] opcional - Entre
to,ccybcc, un máximo de 50 destinatarios. -
reply_toAddress[] opcional - De forma predeterminada, la dirección de respuesta de la conexión.
-
subjectstring obligatorio -
html_bodystring opcional - Envía
html_body,text_bodyo ambos. -
text_bodystring opcional -
from_email, from_namestring opcional - Sustituyen al remitente en este mensaje. Tu proveedor puede rechazar un remitente no verificado.
-
attachment_file_idsinteger[] opcional - Hasta 10 archivos de StickyPrompts de los que seas propietario. Límites de tamaño tras la codificación: SMTP 25 MB, Postmark 10 MB, SendGrid 30 MB, Resend 40 MB.
curl -X POST https://api.stickyprompts.com/api/v1/integration/connections/41/email/send \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": [{ "email": "sales@northwind.example", "name": "Sales team" }],
"subject": "Pipedrive: Deals at Proposal Made",
"html_body": "<p>2 deals at Proposal Made, worth $28,788.</p>",
"text_body": "2 deals at Proposal Made, worth $28,788."
}'
Respuesta 200 {"message_id": "<...>", "provider": "postmark"}: el ID que asigna el propio proveedor, útil para encontrar el mensaje en su panel.
CRM
Las lecturas funcionan en cualquier conexión de CRM. Las escrituras (añadir o editar contactos y crear tratos) requieren allow_write en la conexión.
Contacto: email (obligatorio en todas las escrituras y clave de búsqueda en las ediciones), first_name, last_name, phone, company, custom_fields (específicos de cada proveedor), list_ids (Brevo, ActiveCampaign) y tag_ids (ActiveCampaign). Los resultados se devuelven como {"remote_id", "provider", "contact"}.
Trato (deal): title, value, currency, contact_email (debe pertenecer a un contacto existente) y stage (una etiqueta libre). Los resultados se devuelven como {"remote_id", "provider", "deal"}.
GET /integration/connections/:connectionId/crm/connection
/api/v1/integration/connections/:connectionId/crm/connection {"provider": "pipedrive", "allow_write": true} (más api_url en ActiveCampaign).
GET /integration/connections/:connectionId/crm/contacts
/api/v1/integration/connections/:connectionId/crm/contacts Busca contactos con query (obligatorio) y filtros opcionales del proveedor en notación de corchetes, por ejemplo ?query=priya&filters[organization_id]=1. Pipedrive y ActiveCampaign buscan en texto libre; Brevo busca un único correo electrónico o ID exacto.
GET /integration/connections/:connectionId/crm/contacts/list
/api/v1/integration/connections/:connectionId/crm/contacts/list Recorre los contactos. Usa limit (50 de forma predeterminada) y pagina con cursor (Pipedrive: envía el next_cursor anterior) o con offset (ActiveCampaign, Brevo). Devuelve {"contacts": [...], "has_more": true, "next_cursor": "..."}.
POST /integration/connections/:connectionId/crm/contacts
/api/v1/integration/connections/:connectionId/crm/contacts Añade un contacto. Siempre crea uno nuevo, aunque el correo electrónico ya exista, así que busca antes.
{ "email": "priya.raman@hawthornegoods.example", "first_name": "Priya", "last_name": "Raman", "company": "Hawthorne Goods" }
PATCH /integration/connections/:connectionId/crm/contacts
/api/v1/integration/connections/:connectionId/crm/contacts Edita el contacto que tiene ese email; solo cambian los campos que envías (envía "" para vaciar uno). Nunca crea contactos: 404 "no matching contact was found in the crm to update".
GET /integration/connections/:connectionId/crm/deals
/api/v1/integration/connections/:connectionId/crm/deals Busca tratos por título o por el correo electrónico del contacto vinculado con query, además de filtros del proveedor (en Pipedrive: status, person_id, organization_id, exact_match).
GET /integration/connections/:connectionId/crm/deals/list
/api/v1/integration/connections/:connectionId/crm/deals/list Recorre los tratos, con la misma paginación que los contactos. Los filtros de Pipedrive incluyen stage_id, status, owner_id y pipeline_id.
POST /integration/connections/:connectionId/crm/deals
/api/v1/integration/connections/:connectionId/crm/deals { "title": "Cedar & Pine - Scale plan, 42 stores", "value": 4788, "currency": "USD", "contact_email": "marcus.webb@cedarpine.example", "stage": "Proposal Made" }
404 si ningún contacto tiene ese correo electrónico.
GET /integration/connections/:connectionId/crm/lists, /crm/tags
/api/v1/integration/connections/:connectionId/crm/lists Listas (Brevo, ActiveCampaign) y etiquetas (ActiveCampaign) con la forma [{"id": "12", "name": "Newsletter"}], para usarlas en filtros y en los list_ids / tag_ids de un contacto. Los demás proveedores responden 501.
Google Drive
Las rutas de almacenamiento siempre actúan sobre tu propia conexión personal de Drive y llevan el proveedor en la ruta: /integration/google_drive/storage/.... Los IDs de carpetas y elementos son root o IDs de Drive.
GET /integration/:provider/storage/entries
/api/v1/integration/:provider/storage/entries Lista una carpeta: folder_id (root de forma predeterminada), page_token, page_size (de 1 a 200, 100 de forma predeterminada) y kind (folder o file). Devuelve {"entries": [...], "next_page_token": "", "breadcrumbs": [...]}. Cada elemento tiene id, name, kind, mime_type, size, modified_at, web_url, importable e import_name (Docs y Drawings se importan como PDF, Sheets como XLSX y Slides como PPTX).
GET /integration/:provider/storage/search
/api/v1/integration/:provider/storage/search query (obligatorio), con la misma paginación y los mismos filtros.
GET /integration/:provider/storage/entry/:entryId
/api/v1/integration/:provider/storage/entry/:entryId POST /integration/:provider/storage/import
/api/v1/integration/:provider/storage/import Copia entre 1 y 20 elementos de Drive (entry_ids) en StickyPrompts como archivos normales, opcionalmente vinculados a un project_id. Cada elemento se importa o falla por separado, así que comprueba siempre failed: {"files": [...], "failed": [{"entry_id": "...", "error": "..."}]}. Hasta 100 MB por archivo.
POST /integration/:provider/storage/export
/api/v1/integration/:provider/storage/export Copia un archivo de StickyPrompts del que eres propietario (file_id) en una carpeta de Drive (folder_id; se admite root), con la opción de cambiarle el nombre con name.