Desarrolladores
API de bases de datos
La API de bases de datos te permite leer las columnas y las filas de tus bases de datos, añadir filas nuevas, cambiar celdas y borrar filas escribiendo los valores por nombre de columna, sin pelearte con IDs internos. Es lo que hace posible apuntar un gasto desde el móvil o mandar un formulario a una tabla sin abrir la app.
Antes de empezar
- URL base:
https://app.tylode.com/api/v1 - Manda tu token en la cabecera
Authorization: Bearer tylode_pat_xxx. Los tokens de acceso personal necesitan el plan Pro. Consulta Autenticación. - Obtén el ID del espacio con
GET /api/v1/workspaceso en la app, en Ajustes → Workspace → Datos del espacio → ID del espacio → Copiar. - Cada petición actúa en tu nombre, así que solo ves las bases de datos que ves en la app. Los errores que puede dar cualquier endpoint (
401,403,429) se explican en Errores y convenciones.
Los ejemplos en JavaScript usan la misma función tylode() que la API de páginas:
const BASE = "https://app.tylode.com/api/v1";
const TOKEN = process.env.TYLODE_TOKEN; // tylode_pat_xxx
async function tylode(path, { method = "GET", body } = {}) {
const res = await fetch(BASE + path, {
method,
headers: {
Authorization: `Bearer ${TOKEN}`,
...(body ? { "Content-Type": "application/json" } : {}),
},
body: body ? JSON.stringify(body) : undefined,
});
const json = await res.json();
if (!res.ok) throw new Error(`${res.status}: ${json.error}`);
return json.data;
}Cómo se guardan las filas
Los valores de una fila están en properties, un objeto cuyas claves son los IDs de las columnas (no sus nombres). El valor depende del tipo de columna:
| Tipo de columna | Valor en properties |
|---|---|
text, url, phone | Texto. |
number | Número. |
select | El ID de la opción (no su etiqueta). |
date | Texto con formato YYYY-MM-DD. |
checkbox | true o false. |
person | El ID de usuario de esa persona. |
relation | Array de IDs de filas de la base de datos relacionada. |
formula, rollup, created_by, last_edited | Nada: la app los calcula al mostrar la tabla, así que no están en properties. Para saber cuándo se editó una fila, usa su updated_at. |
Las celdas vacías directamente no aparecen en properties. Para convertir los IDs en nombres y etiquetas, usa las columnas de Listar bases de datos (mira Traducir IDs a nombres). Tienes más detalle de cada tipo en Columnas.
Endpoints
| Método | Ruta | Qué hace | Permiso (scope) |
|---|---|---|---|
| GET | /api/v1/databases | Lista las bases de datos de un espacio con sus columnas y opciones | databases:read |
| GET | /api/v1/databases/:id/rows | Lista las filas de una base de datos | databases:read |
| POST | /api/v1/databases/:id/rows | Añade una fila | databases:write |
| PATCH | /api/v1/databases/:id/rows/:rowId | Cambia celdas de una fila | databases:write |
| DELETE | /api/v1/databases/:id/rows/:rowId | Borra una fila | databases:delete |
Los permisos solo cuentan para las aplicaciones OAuth.
Listar bases de datos
GET /api/v1/databases
Devuelve todas las bases de datos del espacio que puedes ver, cada una con sus columnas en el orden en que se muestran y las opciones de sus columnas de selección.
Permiso (scope): databases:read
| Parámetro | Dónde | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
workspace_id | query | string | Sí | El espacio que quieres listar. |
Cada base de datos trae id, name, page_id (la página que la contiene) y columns. Cada columna trae id, name, type y options; en las columnas select, options es una lista de { id, label, color }, y en el resto va vacía.
curl "https://app.tylode.com/api/v1/databases?workspace_id=WORKSPACE_ID" \
-H "Authorization: Bearer tylode_pat_xxx"const databases = await tylode(`/databases?workspace_id=${WORKSPACE_ID}`);
for (const db of databases) console.log(db.id, db.name);Respuesta 200
{
"data": [
{
"id": "4f8a2c6e-0d3b-4e7a-b1c5-9e2f6a0d4b18",
"name": "Movimientos",
"page_id": "8e3b7f1d-5a9c-4d2e-a6f0-1c4b8e2d7a93",
"columns": [
{ "id": "b1d4e7a0-2c5f-4a8b-9e1d-3f6a9c2e5b80", "name": "Fecha", "type": "date", "options": [] },
{ "id": "c2e5f8b1-3d6a-4b9c-8f2e-4a7b0d3f6c91", "name": "Concepto", "type": "text", "options": [] },
{
"id": "d3f6a9c2-4e7b-4c0d-9a3f-5b8c1e4a7d02",
"name": "Tipo",
"type": "select",
"options": [
{ "id": "e4a7b0d3-5f8c-4d1e-8b4a-6c9d2f5b8e13", "label": "Gasto", "color": "#b4705f" },
{ "id": "f5b8c1e4-6a9d-4e2f-9c5b-7d0e3a6c9f24", "label": "Ingreso", "color": "#6f8a52" }
]
},
{
"id": "a6c9d2f5-7b0e-4f3a-8d6c-8e1f4b7d0a35",
"name": "Categoría",
"type": "select",
"options": [
{ "id": "06b9c2e5-7a0d-4f3b-9c6e-1d4a7b0e3f52", "label": "Casa", "color": "#8a8374" },
{ "id": "17d0e3a6-8c1f-4a4b-9e7d-9f2a5c8e1b46", "label": "Comida", "color": "#7c7f8a" },
{ "id": "28e1f4b7-9d2a-4b5c-8f0e-2a5b8c1f4d63", "label": "Transporte", "color": "#8a7a63" },
{ "id": "39f2a5c8-0e3b-4c6d-9a1f-3b6c9d2a5e74", "label": "Ocio", "color": "#6f7a80" },
{ "id": "4a03b6d9-1f4c-4d7e-8b2a-4c7d0e3b6f85", "label": "Salud", "color": "#857089" },
{ "id": "5b14c7ea-2a5d-4e8f-9c3b-5d8e1f4c7a96", "label": "Ingresos", "color": "#7a8478" },
{ "id": "6c25d8fb-3b6e-4f9a-8d4c-6e9f2a5d8b07", "label": "Otros", "color": "#8a8374" }
]
},
{ "id": "b7d0e3a6-9f2a-4c5d-8a1e-0b3c6f9d2e57", "name": "Importe", "type": "number", "options": [] }
]
}
]
}Errores
| Estado | Cuándo |
|---|---|
400 | Falta workspace_id. |
Listar filas
GET /api/v1/databases/:id/rows
Devuelve todas las filas de una base de datos, ordenadas por position.
Permiso (scope): databases:read
| Parámetro | Dónde | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string | Sí | ID de la base de datos, de Listar bases de datos. |
Cada fila trae id, properties (con los IDs de columna como claves; mira Cómo se guardan las filas), position, created_at y updated_at. No hay paginación ni filtros: recibes todas las filas y las filtras en tu código.
curl https://app.tylode.com/api/v1/databases/DATABASE_ID/rows \
-H "Authorization: Bearer tylode_pat_xxx"Respuesta 200
{
"data": [
{
"id": "0c3f6a9d-2b5e-4a8c-b1d4-7e0a3c6f9b28",
"properties": {
"b1d4e7a0-2c5f-4a8b-9e1d-3f6a9c2e5b80": "2026-09-21",
"c2e5f8b1-3d6a-4b9c-8f2e-4a7b0d3f6c91": "Compra semanal",
"d3f6a9c2-4e7b-4c0d-9a3f-5b8c1e4a7d02": "e4a7b0d3-5f8c-4d1e-8b4a-6c9d2f5b8e13",
"a6c9d2f5-7b0e-4f3a-8d6c-8e1f4b7d0a35": "17d0e3a6-8c1f-4a4b-9e7d-9f2a5c8e1b46",
"b7d0e3a6-9f2a-4c5d-8a1e-0b3c6f9d2e57": 42.5
},
"position": 0,
"created_at": "2026-09-21T18:04:12.381204+00:00",
"updated_at": "2026-09-21T18:04:12.381204+00:00"
}
]
}Si la base de datos no existe o no puedes verla, recibes una lista vacía, no un error.
Traducir IDs a nombres
Las filas usan IDs de columna y de opción. Combínalas con las columnas de GET /databases para obtener algo legible:
const databases = await tylode(`/databases?workspace_id=${WORKSPACE_ID}`);
const db = databases.find((d) => d.name === "Movimientos");
const rows = await tylode(`/databases/${db.id}/rows`);
// Convierte { "<id de columna>": "<id de opción>" } en { "Nombre de columna": "Etiqueta" }
function readable(row, columns) {
const out = { id: row.id };
for (const column of columns) {
const value = row.properties[column.id];
if (value === undefined) continue;
if (column.type === "select") {
const option = (column.options ?? []).find((o) => o.id === value);
out[column.name] = option ? option.label : null;
} else {
out[column.name] = value;
}
}
return out;
}
console.log(rows.map((row) => readable(row, db.columns)));
// [{ id: "0c3f6a9d-…", Fecha: "2026-09-21", Concepto: "Compra semanal",
// Tipo: "Gasto", "Categoría": "Comida", Importe: 42.5 }]Añadir una fila
POST /api/v1/databases/:id/rows
Añade una fila al final de la tabla. Escribes los valores por nombre de columna (o ID) y las selecciones por etiqueta de la opción (o ID); la API se encarga de traducirlos.
Permiso (scope): databases:write
| Parámetro | Dónde | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string | Sí | ID de la base de datos. |
properties | body | object | Sí | Un objeto columna → valor. Las claves pueden ser IDs o nombres de columna. |
Cómo se reconocen las claves:
- Cada clave se compara primero con los IDs de las columnas y después con sus nombres, sin distinguir mayúsculas de minúsculas y sin tener en cuenta los espacios de los extremos.
"importe"encuentra la columna Importe. - Una clave que no coincide con ninguna columna rechaza la petición entera con un
400, y el mensaje de error enumera las columnas que tiene la tabla. - Los valores
nullo""se ignoran, y las columnas que no mandas se quedan vacías. No se rellena nada por ti: si no mandas fecha, la fila se queda sin fecha.
Cómo se convierten los valores según el tipo de columna:
| Tipo de columna | Qué mandar | Qué se guarda |
|---|---|---|
select | La etiqueta de la opción (sin distinguir mayúsculas), p. ej. "comida", o el ID de la opción. | El ID de la opción. Una etiqueta que no existe da un 400 cuyo mensaje enumera las etiquetas válidas. |
number | Un número (24.5) o un texto numérico. También vale la coma decimal: "24,50". | Un número. Cualquier cosa que no sea un número da un 400. |
checkbox | true, "true", 1 o "1" para marcarla. Cualquier otro valor la deja sin marcar. | true / false. |
date | Un texto con formato YYYY-MM-DD, p. ej. "2026-09-23". | El texto tal cual. No se valida, así que respeta ese formato. |
text, url, phone | Un texto. Otros valores se convierten a texto. | Un texto. |
person | El ID de usuario de la persona. | El ID de usuario. |
relation, formula, rollup, created_by, last_edited | No los mandes. Las relaciones no se pueden escribir por la API, y los otros cuatro los calcula la app. | — |
curl -X POST https://app.tylode.com/api/v1/databases/DATABASE_ID/rows \
-H "Authorization: Bearer tylode_pat_xxx" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"Fecha": "2026-09-23",
"Concepto": "Comida con cliente",
"Tipo": "Gasto",
"Categoría": "Comida",
"Importe": "24,50"
}
}'const row = await tylode(`/databases/${DATABASE_ID}/rows`, {
method: "POST",
body: {
properties: {
Fecha: "2026-09-23",
Concepto: "Comida con cliente",
Tipo: "Gasto",
"Categoría": "Comida",
Importe: 24.5,
},
},
});
console.log(row.id);Respuesta 201
La fila tal como se ha guardado: las claves son IDs de columna y las selecciones, IDs de opción.
{
"data": {
"id": "7d36e9fc-4c7f-4a0b-9e5d-8f1a4b7c0d39",
"properties": {
"b1d4e7a0-2c5f-4a8b-9e1d-3f6a9c2e5b80": "2026-09-23",
"c2e5f8b1-3d6a-4b9c-8f2e-4a7b0d3f6c91": "Comida con cliente",
"d3f6a9c2-4e7b-4c0d-9a3f-5b8c1e4a7d02": "e4a7b0d3-5f8c-4d1e-8b4a-6c9d2f5b8e13",
"a6c9d2f5-7b0e-4f3a-8d6c-8e1f4b7d0a35": "17d0e3a6-8c1f-4a4b-9e7d-9f2a5c8e1b46",
"b7d0e3a6-9f2a-4c5d-8a1e-0b3c6f9d2e57": 24.5
},
"position": 3
}
}Errores
| Estado | Cuándo |
|---|---|
400 | Falta properties o no es un objeto. |
400 | Una clave no coincide con ninguna columna. El mensaje enumera las columnas de la tabla. |
400 | Un valor de selección no es ninguna de las opciones de la columna. El mensaje enumera las etiquetas válidas. |
400 | Una columna numérica ha recibido algo que no es un número. |
404 | La base de datos no existe, no puedes verla o no tiene columnas. |
500 | No se ha podido guardar la fila. |
Como los mensajes de error enumeran los nombres y etiquetas válidos, puedes arreglar una integración rota leyendo el error, incluso desde el móvil.
Cambiar celdas de una fila
PATCH /api/v1/databases/:id/rows/:rowId
Cambia solo las columnas que mandas. Las claves y los valores funcionan igual que al añadir una fila: nombre o ID de columna, etiqueta o ID de opción, coma decimal en los números y true o "1" en las casillas. El resto de la fila se queda como está, así que si otra persona cambia otra columna mientras tanto, su cambio no se pierde.
Permiso (scope): databases:write
| Parámetro | Dónde | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string | Sí | ID de la base de datos. |
rowId | path | string | Sí | ID de la fila, de Listar filas. |
properties | body | object | Sí | Un objeto columna → valor, con al menos una columna. |
A diferencia de al añadir una fila, null y "" no se ignoran: vacían la celda. Por ejemplo, { "properties": { "Fecha": null } } deja la fila sin fecha.
curl -X PATCH https://app.tylode.com/api/v1/databases/DATABASE_ID/rows/ROW_ID \
-H "Authorization: Bearer tylode_pat_xxx" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"Importe": "26,90",
"Categoría": "Ocio"
}
}'const row = await tylode(`/databases/${DATABASE_ID}/rows/${ROW_ID}`, {
method: "PATCH",
body: { properties: { Importe: 26.9, "Categoría": "Ocio" } },
});Respuesta 200
La fila entera tal como queda, con las claves como IDs de columna y las selecciones como IDs de opción.
{
"data": {
"id": "7d36e9fc-4c7f-4a0b-9e5d-8f1a4b7c0d39",
"properties": {
"b1d4e7a0-2c5f-4a8b-9e1d-3f6a9c2e5b80": "2026-09-23",
"c2e5f8b1-3d6a-4b9c-8f2e-4a7b0d3f6c91": "Comida con cliente",
"d3f6a9c2-4e7b-4c0d-9a3f-5b8c1e4a7d02": "e4a7b0d3-5f8c-4d1e-8b4a-6c9d2f5b8e13",
"a6c9d2f5-7b0e-4f3a-8d6c-8e1f4b7d0a35": "39f2a5c8-0e3b-4c6d-9a1f-3b6c9d2a5e74",
"b7d0e3a6-9f2a-4c5d-8a1e-0b3c6f9d2e57": 26.9
},
"position": 3
}
}Errores
| Estado | Cuándo |
|---|---|
400 | El ID de la base de datos o de la fila no es válido. |
400 | Falta properties, no es un objeto o va vacío. |
400 | Una clave no coincide con ninguna columna, un valor de selección no es ninguna de las opciones o una columna numérica ha recibido algo que no es un número. Igual que al añadir, el mensaje enumera lo que hay. |
403 | Puedes ver la fila pero no cambiarla (por ejemplo, porque tu rol es de solo lectura). |
404 | La base de datos o la fila no existen, no puedes verlas o la base de datos no tiene columnas. |
Borrar una fila
DELETE /api/v1/databases/:id/rows/:rowId
Borra una fila de una base de datos. Las filas no tienen papelera: no se puede recuperar.
Permiso (scope): databases:delete
| Parámetro | Dónde | Tipo | Obligatorio | Descripción |
|---|---|---|---|---|
id | path | string | Sí | ID de la base de datos. |
rowId | path | string | Sí | ID de la fila. |
curl -X DELETE https://app.tylode.com/api/v1/databases/DATABASE_ID/rows/ROW_ID \
-H "Authorization: Bearer tylode_pat_xxx"Respuesta 200
{ "data": { "id": "7d36e9fc-4c7f-4a0b-9e5d-8f1a4b7c0d39", "papelera": false } }Errores
| Estado | Cuándo |
|---|---|
400 | El ID de la base de datos o de la fila no es válido. |
404 | La fila no existe, no es de esa base de datos o no puedes borrarla. |
Ejemplo: apuntar un gasto en tu tabla de Finanzas
La plantilla Finanzas (mira Plantillas) crea una página con una base de datos llamada Movimientos y estas columnas: Fecha (fecha), Concepto (texto), Tipo (selección: Gasto, Ingreso), Categoría (selección: Casa, Comida, Transporte, Ocio, Salud, Ingresos, Otros) e Importe (número). Si usas Tylode en otro idioma, se crea en ese idioma si está traducido y, si no, en inglés (Transactions: Date, Description, Type, Category, Amount). Si has cambiado el nombre de alguna columna u opción, usa los tuyos en las peticiones de abajo.
Busca el ID de la base de datos:
bashcurl "https://app.tylode.com/api/v1/databases?workspace_id=WORKSPACE_ID" \ -H "Authorization: Bearer tylode_pat_xxx"Busca la entrada cuyo
nameesMovimientosy copia suid. Solo tienes que hacerlo una vez.Añade el gasto:
jsconst DATABASE_ID = "DATABASE_ID"; async function logExpense({ concept, amount, category, date = new Date() }) { return tylode(`/databases/${DATABASE_ID}/rows`, { method: "POST", body: { properties: { Fecha: date.toLocaleDateString("sv-SE"), // YYYY-MM-DD en tu hora local Concepto: concept, Tipo: "Gasto", "Categoría": category, Importe: amount, }, }, }); } await logExpense({ concept: "Taxi al aeropuerto", amount: "32,80", category: "Transporte" });Abre la página Finanzas en la app: la fila nueva está al final de la tabla.
La configuración regional sv-SE es solo un atajo para obtener una fecha YYYY-MM-DD en tu zona horaria. Si quieres hacer lo mismo desde un iPhone sin escribir código, sigue la receta de Atajos de Apple.
Lo que la API de bases de datos no hace
- No hay endpoint para crear ni borrar bases de datos o columnas. Eso se hace en la app (mira Bases de datos).
- No expone vistas, filtros ni ordenaciones: recibes las filas tal cual.
- Las relaciones se pueden leer, pero no escribir.