Saltar al contenido
OSOKORO
Para desarrolladores

Llevarte tus registros.

La gente de tu lista de espera son tus contactos, no los nuestros. Hay cuatro maneras de moverlos a tu propia aplicación, y no necesitas pedirnos que activemos ninguna.

Cuál te conviene

Una mudanza de una sola vez

Descarga el CSV. Es la lista completa, con respuestas, conteos de referidos y parámetros UTM. En Starter, Everything y Studio.

Mantener tu propia base de datos al día

Consulta la API REST con updated_since. Esta es la opción sobre la que construir. En Everything y Studio.

Reaccionar en el momento en que alguien se registra

Apunta un webhook a tu propio endpoint. Firmado, con reintentos, y puedes ver cada entrega.

Conectarlo con algo que no escribiste tú

Zapier, para las herramientas cuya API no vale la pena llamar.

El CSV

En la pantalla de Registros de una lista, elige Exportar. Una fila por registro, una columna por pregunta, y una marca de orden de bytes para que Excel lea bien los nombres en UTF-8 en vez de destrozarlos. Los valores que empiezan con =, +, - o @ llevan un prefijo, para que la hoja de cálculo trate la respuesta como texto y no como fórmula.

Sirve para una migración que haces una vez. Para cualquier cosa continua, usa la API: un CSV es una foto fija y no puede decirte que alguien confirmó su dirección ayer.

Autenticarte en la API

Crea una clave en Ajustes → Claves de API. Se muestra una sola vez. Guárdala en una variable de entorno en vez de escribirla en la terminal — una clave pegada termina en tu historial — y envíala como bearer token:

export OSOKORO_API_KEY=…            # la clave que acabas de ver

curl https://osokoro.com/api/v1/signups?project=your-waitlist \
  -H "Authorization: Bearer $OSOKORO_API_KEY"

Las claves pertenecen a la organización que las creó y no tienen alcance por proyecto: elige la lista con project. El plan se verifica en cada petición, no cuando se creó la clave, así que una clave deja de funcionar en cuanto una organización pierde el acceso a la API y vuelve a funcionar si lo recupera. Revocar una clave surte efecto de inmediato.

Cada respuesta trae x-ratelimit-limit, x-ratelimit-remaining y x-ratelimit-reset, así puedes bajar el ritmo antes de que te lo pidan. El cupo por hora es Everything: 1000, Studio: 10.000. Cada respuesta trae también x-request-id; cítalo si reportas un problema y podremos encontrar la petición.

GET /api/v1/signups

ParámetroAceptaQué hace
projectobligatorioEl slug de la lista, tal como aparece en la URL de su página.
limit1–500, por defecto 100Cuántas filas devolver.
cursoropacoEl next_cursor de la página anterior. Omítelo en la primera.
sortcreated_at | updated_atQué marca de tiempo ordena los resultados. Por defecto, created_at.
updated_sinceISO 8601Solo las filas cambiadas en ese instante o después. Esto es lo que hace posible una sincronización incremental.
verifiedtrueSolo los registros que confirmaron su dirección de correo.

Una página se ve así:

{
  "data": [
    {
      "id": "8f0c…",
      "email": "someone@example.com",
      "verified": true,
      "verified_at": "2026-08-02T09:14:22.104Z",
      "position_code": "8F2QK",
      "referral_count": 3,
      "referred": false,
      "answers": { "price": "$10", "need": "Offline mode" },
      "utm": { "utm_source": "producthunt" },
      "created_at": "2026-08-01T18:02:11.882Z",
      "updated_at": "2026-08-02T09:14:22.104Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJzb3J0Ijoi…",
  "request_id": "5c1f…"
}
CampoSignificado
idIdentificador estable. Úsalo como tu propia clave foránea.
emailLa dirección tal como se envió.
verifiedSi hicieron clic en el enlace de su correo de confirmación.
verified_atCuándo lo hicieron, o null.
position_codeSu código de referido — el que lleva su enlace para compartir.
referral_countCuántos registros confirmados llegaron por su enlace.
referredSi llegaron por el enlace de otra persona.
answersUn objeto con las claves de tus preguntas.
utmLos parámetros UTM presentes cuando llegaron.
created_atCuándo se registraron.
updated_atCuándo cambió la fila por última vez. Sincroniza a partir de esto.

Leer la lista completa

Sigue next_cursor hasta que has_more sea false. El cursor es un keyset sobre el campo de orden y el id de la fila, así que las filas que comparten marca de tiempo — cosa que una importación masiva produce por miles — nunca se saltan en un borde de página ni se devuelven dos veces.

let cursor = null;
const everyone = [];

do {
  const url = new URL("https://osokoro.com/api/v1/signups");
  url.searchParams.set("project", "your-waitlist");
  url.searchParams.set("limit", "500");
  if (cursor) url.searchParams.set("cursor", cursor);

  const response = await fetch(url, {
    headers: { authorization: `Bearer ${process.env.OSOKORO_API_KEY}` },
  });
  if (!response.ok) throw new Error((await response.json()).error.code);

  const page = await response.json();
  everyone.push(...page.data);
  cursor = page.next_cursor;
} while (cursor);

Mantenerse al día después

Guarda el updated_at más alto que hayas visto y devuélvelo como updated_since, ordenando por updated_at. Eso devuelve tanto las filas que cambiaron como las nuevas — así te enteras de que alguien confirmó su dirección o se dio de baja, algo que una consulta por created_at nunca puede decirte.

const url = new URL("https://osokoro.com/api/v1/signups");
url.searchParams.set("project", "your-waitlist");
url.searchParams.set("sort", "updated_at");
url.searchParams.set("updated_since", lastSyncedAt);  // ISO 8601

Usa id como clave de tu lado y haz upsert. Los registros nunca se renumeran, así que un id que guardaste el mes pasado sigue apuntando a la misma persona.

Cuando algo sale mal

Todos los fallos tienen la misma forma — { "error": { "code", "message", "request_id" } } — así que puedes ramificar por code y nunca por el texto. Una lista que pertenece a otra organización responde exactamente igual que una que no existe; eso es deliberado, para que una clave no sirva para descubrir los slugs de otra persona.

EstadoCódigoSignificado
401unauthorizedLa clave es desconocida, fue revocada, o es una de prueba enviada como real.
402plan_without_apiEl plan de la organización no incluye acceso a la API.
429quota_exceededSe agotaron las peticiones de la hora. retry-after dice cuánto esperar.
400project_requiredFalta el parámetro `project`.
404not_foundNo hay ninguna lista con ese slug en esta cuenta.
400invalid_limit · invalid_sort · invalid_updated_since · invalid_cursorEl parámetro indicado no era utilizable. El mensaje dice cómo debería ser.
503unavailableNo pudimos verificar la clave. Reintenta; esto no es un rechazo.

Webhooks, para el momento en que pasa

Agrega un endpoint en los ajustes de una lista y elige qué eventos debe recibir:

  • signup.created
  • signup.verified
  • signup.unsubscribed
  • perk.claimed
  • broadcast.sent

El cuerpo es { "event", "data" }. Cada entrega lleva x-osokoro-event y x-osokoro-signature, que contiene una marca de tiempo y un HMAC-SHA256 de timestamp.body firmado con el secreto de tu endpoint. Verifícalo antes de confiar en una petición, y compara en tiempo constante. Una entrega fallida se reintenta con intervalos crecientes durante alrededor de medio día, y cada intento aparece con su estado para que veas qué pasó en vez de adivinarlo.

Los webhooks te avisan de los cambios. No son una forma de leer la lista que ya tienes — combínalos con una pasada completa por la API, o con el CSV.

Borrar tus datos

Llevarte una copia no te obliga a dejar otra atrás. Borrar una organización desde Ajustes elimina sus listas, registros y respuestas; lo que queda es un registro agregado con conteos y sin direcciones. Consulta el aviso de privacidad para saber qué guarda ese registro y por qué.