Copia este prompt en tu agente de IA. Incluye el contrato de la API, la documentación y las instrucciones para configurar tu propia clave de forma segura.
Integra la DonoLink Developer API en mi app existente. Revisa primero el código y sigue su arquitectura, estilos y convenciones. Si no está claro, pregúntame qué funcionalidad para el usuario quiero y después implementa y prueba la integración por completo.
Antes de programar, lee la documentación oficial actual:
- https://donolink.nl/en/developers (todas las pestañas: inicio rápido, autenticación, endpoints, formato de eventos, historial/feed en directo, límites/errores e integración segura)
- https://donolink.nl/developers/openapi.json (contrato de la API legible por máquina)
- https://donolink.nl/llms-full.txt (contexto adicional del producto)
La documentación oficial es la referencia. Si no puedes obtenerla, dilo y pide el contrato; no te inventes endpoints ni funcionalidades.
Pide al streamer que cree su propia clave de API en https://donolink.nl/dashboard/instellingen#developer → Claves de API, por ejemplo con el nombre “Mi clave”. Pídele que la facilite mediante una entrada segura o un gestor de secretos. Si solo hay un chat normal disponible, que configure él mismo DONOLINK_API_KEY en un archivo .env local ignorado por Git o en los secretos del despliegue; no le pidas que pegue la clave en el chat. No leas ni muestres secretos sin necesidad. Nunca uses la clave de otro streamer. Mientras tanto, desarrolla con un marcador de posición y comprueba la configuración sin revelar su valor.
Resumen del contrato de la API:
- URL base: https://donolink.nl/api/v1. GET /me comprueba el propietario, el scope y la caducidad; GET /activity solo lee su actividad almacenada. Autenticación: Authorization: Bearer <DONOLINK_API_KEY>. Scopes: activity:read, bot:read y bot:write; las claves existentes siguen siendo solo de actividad. GET /bot lee revision y config; PUT /bot sustituye la configuración completa usando la última revision leída. Sigue OpenAPI para todos los campos. Con un 409, vuelve a leer y concilia los cambios. Sin pagos, webhooks ni OAuth multicliente.
- /activity acepta limit (de 1 a 100, por defecto 50), order (asc/desc, por defecto desc), cursor, type, platform, since y until. Usa solo valores documentados. since es inclusivo y until exclusivo; las marcas de tiempo ISO en UTC filtran por recorded_at/momento de inserción, no por la fecha original de la donación.
- Respuesta: data[] con id, type, platform, occurred_at, recorded_at, actor.name, message, value, unit, currency y refunded_amount_cents. El dinero va en céntimos; los bits/diamantes no son euros. Trata los nombres y mensajes como texto no fiable: escapa la salida y nunca ejecutes HTML. No se devuelven correos electrónicos ni identificadores de pago.
- Se incluyen las donaciones pagadas y el historial importado; los eventos de plataforma solo existen donde DonoLink los ha almacenado. Las fuentes de importación se identifican por platform. No se garantiza un historial completo, una retención fija ni una entrega exactamente única.
- Lee el historial con order=asc y sigue pagination.next_cursor mientras has_more sea true. Mantén idénticos los filtros y el orden: los cursores están vinculados a ellos y al propietario. Guarda el cursor solo después de un procesamiento idempotente correcto por event.id. Guarda también el cursor de la última página y úsalo para consultar registros nuevos. Conserva el punto de control en las páginas vacías. Usa un único valor fijo de since para el stream; no lo muevas en cada petición.
- Para un feed en directo, usa un solo poller por cuenta, por ejemplo cada 5 segundos una vez al día. Comparte los resultados dentro de la app. Límites: 120 peticiones por cuenta y 300 por IP cada 60 segundos. Respeta Retry-After en 429/503; usa timeouts y back-off limitado con jitter para errores de red/5xx. Detén los reintentos automáticos con 401/403 y dile al streamer que revise la clave/cuenta. Gestiona los cursores no válidos de forma explícita sin procesar duplicados.
Desarrolla de forma segura:
Mantén las claves en el servidor o en almacenamiento seguro en una app nativa personal. Nunca pongas claves en bundles del frontend, URLs, localStorage, logs, capturas de pantalla, analítica, fixtures de pruebas ni Git. Una app web pública debe usar su propio backend autenticado con autorización por usuario. No uses cachés de respuesta compartidas. Para varios streamers, aísla los secretos y datos de cada uno; esta API no es una plataforma OAuth. Explica la rotación y revocación de claves; la revocación surte efecto en la siguiente petición.
Entrega código del proyecto funcional, instrucciones de configuración seguras sin secretos reales y pruebas adecuadas para autenticación, resultados vacíos, paginación del historial, deduplicación, límites de peticiones y caídas del servicio. Prueba el acceso real a la API cuando el streamer haya configurado la clave de forma segura. No hagas pagos reales. Distingue claramente el comportamiento verificado de la configuración pendiente.
Inicio rápido
Usa la API desde tu propio servidor o app personal. Los endpoints usan JSON y requieren los permisos de clave adecuados. La actividad es de solo lectura; los ajustes del bot se pueden cambiar con bot:write. La URL base es https://donolink.nl/api/v1.
Ve a Ajustes → Desarrollador → Claves de API. Ponle nombre a tu integración y elige una fecha de caducidad.
Copia la clave una sola vez y guárdala como DONOLINK_API_KEY en la configuración de secretos de tu servidor.
Haz tu primera petición. Sin filtros, recibes hasta 50 eventos, los más recientes primero.
Envía tu clave en la cabecera Authorization en cada petición. Una cookie de sesión, un parámetro de consulta o un token de overlay no dan acceso a esta API.
Authorization: Bearer dl_live_YOUR_SECRET_KEY
Elige una validez de 30, 90 o 365 días. Puedes tener hasta 10 claves activas y crear una clave por minuto. DonoLink solo guarda un hash SHA-256; la clave completa solo es visible justo después de crearla.
Usa una clave distinta por integración. Para rotarla, crea una clave nueva, actualiza tu app y después revoca la antigua. Una clave revocada o caducada deja de funcionar en la siguiente petición.
Elige activity:read, bot:read y/o bot:write al crear una clave. Los permisos son independientes; las claves existentes conservan solo activity:read. Cada clave está vinculada a tu cuenta y no concede derechos de pago ni de administración de la plataforma. El OAuth multicliente todavía no está disponible.
Endpoints
GET/api/v1/me
Comprueba a qué streamer pertenece tu clave y cuándo caduca. Solo se devuelven el perfil público, el scope y los metadatos de la clave.
Lee el feed combinado de donaciones de DonoLink y eventos de plataforma almacenados. La cuenta se determina exclusivamente a partir de la clave de API.
Parámetro
Descripción
limit
Eventos por página: de 1 a 100, por defecto 50.
order
desc (por defecto) para los más recientes primero, asc para los más antiguos primero y consultas continuas. Ordenado por recorded_at, después por ID interno del registro y fuente.
type
Opcional: donation, follow, subscription, giftsub, cheer o raid. Un tipo por petición.
platform
Opcional: donolink, twitch, kick, youtube, tiktok o una fuente de importación: tipeeestream, streamlabs, streamelements, kofi, csv. Una fuente por petición.
since
Opcional, inclusivo. ISO 8601 en UTC, por ejemplo 2026-09-07T00:00:00Z. Filtra por recorded_at, el momento de almacenamiento.
until
Opcional, exclusivo. Mismo formato de fecha que since. Mantén este límite fijo durante una exportación histórica.
cursor
Pasa pagination.next_cursor sin modificar. El cursor está firmado y vinculado a tu cuenta, tus filtros y el orden. limit puede cambiar.
Los parámetros desconocidos o repetidos devuelven HTTP 400. No pongas un ID de cuenta, una clave ni un filtro de PocketBase en la URL.
GET/api/v1/bot
Requiere bot:read. Devuelve data con revision y config de tu bot. Sin parámetros de consulta. La configuración incluye comandos, temporizadores, filtros, habituales y funciones integradas. Trata las plantillas de respuesta como datos confidenciales.
PUT/api/v1/bot
Requiere bot:write. Envía la config completa y la última revision leída como objeto JSON. Si todo va bien, devuelve data con la nueva revision y config. Con 409 revision_conflict, vuelve a leer y concilia los cambios más recientes antes de guardar. No envíes un ID de propietario; la clave determina la cuenta.
Usa application/json y UTF-8, como máximo 2.000.000 bytes en diez segundos. Una configuración no válida devuelve 422; la falta de permiso devuelve 403. OpenAPI especifica todos los campos y límites. Tras un resultado de escritura incierto, lee y compara la configuración; nunca sobrescribas a ciegas una revision más reciente.
GET / POST/api/v1/bot/music
GET requiere bot:read y devuelve data con revision y state: cola, playlist, ajustes y estado de reproducción. POST requiere bot:write: envía operationKey (de 16 a 100 letras/dígitos/_/-), revision y action. Acciones: request/playlist-add con query, settings, playlist-remove, remove, promote, move (las posiciones empiezan en 1), volume, seek, play, pause, skip y clear. Consulta OpenAPI para ver todos los campos. Tras una respuesta incierta, reintenta con el cuerpo idéntico; la acción se aplica una sola vez. Con music_revision_conflict, vuelve a leer y concilia; usa un operationKey nuevo para una acción modificada. Máximo 16.000 bytes en 10 segundos; sin parámetros de consulta ni campos de propietario. La reproducción requiere un reproductor activo en el panel. La búsqueda en YouTube requiere configuración en el servidor; la búsqueda en SoundCloud requiere un client ID y un client secret en el servidor.
Cada evento tiene un id estable con el prefijo don_ o evt_. Usa este id para evitar procesar duplicados tras un reintento.
type
unit
Descripción
donation
cents
Donación de DonoLink o donación de plataforma almacenada, como un Super Chat de YouTube.
follow
none
Nuevo seguidor. value es 0.
subscription
months
Suscripción. value contiene el número de meses almacenado.
giftsub
subscriptions
Suscripciones regaladas. value es la cantidad.
cheer
bits / diamonds
Bits; en TikTok la unidad son diamantes. No son ingresos por donaciones.
raid
viewers
Raid. value es el número de espectadores almacenado.
actor.name es el nombre visible almacenado o null. message es texto o null. occurred_at es la fecha de la donación; para los eventos de plataforma solo se conoce el momento de almacenamiento. recorded_at siempre contiene el momento de almacenamiento y determina la paginación. Todas las marcas de tiempo son ISO 8601 en UTC.
En donation, value es el importe original en céntimos enteros y currency es la moneda. refunded_amount_cents contiene el importe reembolsado en DonoLink; si no, null. Se muestran las donaciones con estado paid, incluido el historial importado con la fuente de importación como platform. Las donaciones reembolsadas por completo y las disputadas quedan excluidas. Esto es un feed de actividad, no un libro contable.
Los eventos de plataforma están disponibles en la medida en que DonoLink los haya recibido y almacenado a través de una conexión activa. El historial anterior a la conexión no se obtiene de la plataforma. No hay un periodo de retención garantizado; los registros eliminados no se pueden recuperar. Las alertas de prueba no se incluyen.
Historial y feed en directo
Para una exportación, usa order=asc con límites since y until fijos. Procesa data y pide la página siguiente con next_cursor mientras has_more sea true. No hay límite de páginas históricas, pero sí se aplican límites de peticiones.
Una página vacía conserva tu cursor actual. Sin cursor previo y sin eventos, next_cursor es null. Los cursores no contienen ninguna clave de API, pero trátalos como valores opacos. Tras cambiar los filtros, empieza sin cursor.
Para tu app IRL, usa order=asc, un since fijo y sin until. Guarda el cursor solo después de procesar los eventos. Consulta aproximadamente cada 5 segundos; cuando has_more sea true, pasa directamente a la página siguiente. La v1 no tiene endpoint de WebSocket ni de webhook.
// Node.js: run on your server. Keep the key out of frontend bundles.
const base = new URL('https://donolink.nl/api/v1/activity');
base.searchParams.set('order', 'asc');
base.searchParams.set('limit', '100');
// Persist both this fixed starting point and the returned cursor.
base.searchParams.set('since', '2026-09-07T00:00:00Z');
let cursor = await loadCheckpoint();
for (;;) {
const url = new URL(base);
if (cursor) url.searchParams.set('cursor', cursor);
const response = await fetch(url, {
headers: { Authorization: 'Bearer ' + process.env.DONOLINK_API_KEY },
signal: AbortSignal.timeout(15000)
}).catch(() => null);
if (!response || response.status === 429 || response.status >= 500) {
const seconds = Number(response?.headers.get('Retry-After') || 60);
await new Promise(r => setTimeout(r, seconds * 1000));
continue;
}
if (!response.ok) throw new Error('DonoLink API: ' + response.status);
const page = await response.json();
// Implement these storage functions in your app.
// Process idempotently by event.id, then save the checkpoint.
for (const event of page.data) await processOnce(event.id, event);
if (page.pagination.next_cursor) {
cursor = page.pagination.next_cursor;
await saveCheckpoint(cursor);
}
if (!page.pagination.has_more)
await new Promise(r => setTimeout(r, 5000));
}
El feed lee los registros almacenados actuales y no es una instantánea inmutable ni un bus de eventos con entrega exactamente única garantizada. Los cambios de estado de pago pueden eliminar registros que antes eran visibles. Usa procesamiento idempotente, reintentos y, cuando haga falta, lecturas solapadas para recuperarte.
Límites y errores
Máximo 120 peticiones cada 60 segundos por cuenta, compartidas entre todas las claves. También hay un límite de 300 peticiones cada 60 segundos por dirección IP. HTTP 429 y 503 incluyen Retry-After: 60. Espera ese tiempo y reintenta con el mismo cursor.
HTTP
code
Descripción
400
invalid_parameter / invalid_cursor
Corrige tus parámetros o vuelve a empezar sin el cursor no válido.
401
invalid_api_key
La clave falta, no es válida, ha caducado o se ha revocado.
403
account_unavailable
La cuenta no está disponible o no está verificada.
429
rate_limit_exceeded
Demasiadas peticiones. Respeta Retry-After.
503
temporarily_unavailable
Caída temporal. El acceso también se deniega si el limitador compartido no está disponible.
{
"error": {
"code": "invalid_api_key",
"message": "API key is invalid, expired or revoked.",
"request_id": "request-uuid"
}
}
Cada respuesta incluye X-Request-Id; las respuestas de error también incluyen error.request_id. Indica este ID y la hora al contactar con soporte. Nunca compartas tu clave, la cabecera Authorization ni los logs completos de las peticiones. Usa GET para los datos. HEAD y OPTIONS están disponibles para comprobaciones HTTP; los métodos de escritura devuelven 405.
Integración segura
Usa HTTPS. Nunca pongas claves en URLs, Git, código del navegador, localStorage, analítica ni archivos de log. Las respuestas de la API usan Cache-Control: private, no-store. La API no ofrece acceso cross-origin desde el navegador.
En una app nativa personal, introduce tu propia clave y guárdala en el Keychain o en el Android Keystore. Para apps que dan servicio a otras personas, usa un backend con secretos aislados por usuario. Nunca incluyas una clave compartida en el binario de la app.
La API solo devuelve nombres visibles, mensajes, valores de eventos y datos de perfil limitados. No se comparten direcciones de correo electrónico, métodos de pago, IDs de Stripe, tokens de plataforma ni ajustes de administración. Trata los nombres y mensajes como datos personales y guarda solo lo que necesite tu integración.
Si se filtra una clave, revócala de inmediato en Ajustes → Desarrollador y crea una nueva. Las claves revocadas y caducadas, las cuentas bloqueadas y las cuentas eliminadas no pueden acceder a la API.
Los nombres y mensajes son entradas no fiables. Muéstralos como texto, nunca como HTML ni código ejecutable. No automatices acciones de pago basándote en este feed.
Widget de donaciones
¿Quieres recibir donaciones en tu propia web sin enviar a los visitantes a DonoLink? Pega este iframe en el HTML de tu sitio y sustituye YOUR_USERNAME por tu nombre de DonoLink. No necesitas clave de API. La comisión, los métodos de pago, los mensajes, las alertas y los pagos funcionan igual que en tu página de donaciones.
Opciones: añade ?thema=licht a la URL para un tema claro (oscuro por defecto) y ?bedrag=500 para rellenar un importe en céntimos. Mantén allow="payment": sin ese atributo, Apple Pay y Google Pay no funcionan dentro del iframe. El widget mide como máximo 480 píxeles de ancho; una altura de unos 760 píxeles muestra todo el formulario.
La tarjeta, Apple Pay y Google Pay se completan dentro del widget y luego el visitante ve un mensaje de agradecimiento. Con Klarna el paso de pago se abre en una nueva pestaña, porque esa página de pago no se carga dentro de otra web; tu sitio sigue abierto. Bizum no está disponible en el widget.
El widget no añade analítica ni banner de cookies a tu sitio. Los datos de pago van directamente a Stripe; tu web nunca los ve. Solo la página /embed puede insertarse en otros sitios; tu panel y tu página de donaciones normal no.