Referencia de API para servidores
API de publicación de SosoAPI: autenticación, solicitudes y resultados
Crea una integración de publicación en el servidor con una publicación programada por destino. Esta referencia cubre el contrato de la API de publicación revisado el 20 de octubre de 2026. Los ejemplos contienen marcadores y no se ejecutan en esta página.
Leer en MarkdownRevisado por el equipo de SosoAPI ·
1. Autentícate desde tu servidor
Crea una clave API en SosoAPI App → Settings → Agents & API Access. Guárdala como SOSOAPI_API_KEY en el entorno de tu servidor. Las solicitudes usan Authorization: Bearer <your-key> y Content-Type: application/json. Nunca incluyas la clave en un sitio web, paquete de aplicación móvil, URL o repositorio público.
Todos los endpoints siguientes usan POST con cuerpo JSON. Las claves y concesiones de agentes delegados quedan limitadas por sus capacidades, espacios permitidos y pertenencia actual del propietario. Las lecturas necesitan agent:operate y al menos acceso viewer; las mutaciones de publicación necesitan agent:publish y al menos acceso editor. Una concesión no puede ampliar los derechos del propietario.
Usa el espacio de trabajo y las cuentas conectadas actuales de la aplicación, o llama a /identity-api/workspace/list y /identity-api/user/social-accounts/list para encontrar IDs que tengas autorización para usar. No adivines el ID del espacio de trabajo o la cuenta de un cliente.
// Node.js 20+; run only on your server.
const apiKey = process.env.SOSOAPI_API_KEY;
if (!apiKey) throw new Error("Set SOSOAPI_API_KEY on your server");
const apiBase = "https://gqeshlawtcexsapabaui.supabase.co/functions/v1";
async function sosoPost(path, body) {
const response = await fetch(apiBase + path, {
method: "POST",
headers: {
Authorization: "Bearer " + apiKey,
"Content-Type": "application/json"
},
body: JSON.stringify(body)
});
const result = await response.json();
if (!response.ok) {
const requestId = result.request_id ?? response.headers.get("x-request-id");
// Handler errors use code/message; schema-validation 400s instead use
// success:false and error:{name:"ZodError",message:...}.
// Preserve status, available code and requestId in server diagnostics.
// Never log the key or an Authorization header.
throw new Error("SosoAPI " + response.status + " (" +
(requestId ?? "no request id") + ")");
}
return result;
}2. Mapa de endpoints de publicación
Estas rutas son relativas a la base API usada por la función auxiliar del servidor. Consulta el detalle de la publicación programada para obtener su versión actual, acciones disponibles y resultado de publicación.
| Ruta | Solicitud | Respuesta y límites |
|---|---|---|
| /studio-api/scheduled-posts/list | workspace_id; project_id, status, par de horas, limit y cursor opcionales. | posts, next_cursor, returned_count. Lectura limitada al espacio de trabajo. |
| /studio-api/scheduled-posts/get | id. | post, delivery, actions, attempts, outcomes. Consulta aquí la versión actual. |
| /studio-api/scheduled-posts/eligibility | workspace_id, targets; include_quota opcional. | destinos y cuota opcional. Comprobación previa de solo lectura, no una reserva. |
| /studio-api/scheduled-posts/create | workspace_id, cuenta exacta, provider_payload, contenido, scheduled_at, idempotency_key. | post y deduped. 201 para una programación nueva; 200 para una repetición coincidente. |
| /studio-api/scheduled-posts/bulk-create | workspace_id y 1–50 targets, cada uno con su propio idempotency_key. | batch_id, successes, failures. 201 cuando al menos uno tiene éxito; 422 cuando ninguno tiene éxito. Examina ambos arrays. |
| /studio-api/scheduled-posts/update | id, expected_version, patch; provider_payload coincidente opcional. | post. Se rechaza una versión desactualizada; el destino es inmutable. |
| /studio-api/scheduled-posts/publish-now | id y expected_version. | post. Solicita ejecución inmediata; no demuestra publicación. |
| /studio-api/scheduled-posts/retry | id, expected_version; scheduled_at futuro opcional. | post. Recuperación solo de fallidos, sujeta a evidencias; nunca un reenvío a ciegas. |
| /studio-api/scheduled-posts/cancel | id y expected_version. | post. Cancela trabajo sin publicar; la cancelación remota puede seguir pendiente. |
| /studio-api/scheduled-posts/delete | id y expected_version. | post. Los solicitantes de máquina no pueden eliminar contenido del proveedor ya publicado. |
| /studio-api/scheduled-posts/withdraw-delete | id y expected_version. | post. Retira una solicitud detenida de eliminación de Instagram Login, solo de verificación; conserva el contenido del proveedor. |
3. Lista y pagina sin perder el alcance
limit tiene un valor predeterminado de 50 y acepta 1–200. El status opcional puede ser scheduled, publishing, published, failed, expired, needs_reschedule, cancelled o deleted. scheduled_from y scheduled_to deben proporcionarse juntos como instantes ISO-8601 con desplazamiento horario; el final debe ser posterior al inicio.
next_cursor es null o un objeto con scheduled_at e id. Devuelve ese objeto completo como cursor, junto con el mismo espacio de trabajo y filtros. Este endpoint no admite filtros page, offset, platform ni social_account_id.
await sosoPost("/studio-api/scheduled-posts/list", {
"workspace_id": "<workspace-uuid>",
"limit": 50
});const first = await sosoPost("/studio-api/scheduled-posts/list", {
workspace_id: "<workspace-uuid>", limit: 50
});
if (first.next_cursor) {
const next = await sosoPost("/studio-api/scheduled-posts/list", {
workspace_id: "<workspace-uuid>", limit: 50, cursor: first.next_cursor
});
// Consume next.posts; continue only while next_cursor is not null.
}4. Valida y crea una publicación revisada
Sustituye cada marcador por valores de tu alcance autorizado y una hora futura. Al ejecutarse, este ejemplo programa una publicación de texto real en Threads. Obtén la confirmación de la persona sobre la cuenta, el contenido y la hora antes de la llamada de creación.
Establece provider_payload.platform y los publish_options de esa plataforma. El servidor comprueba que coincidan con la cuenta elegida. Puedes incluir project_id para registrar la procedencia; el proyecto no concede acceso de publicación ni controla la programación.
| Campo | Contrato |
|---|---|
| workspace_id / social_account_id | UUID obligatorios del espacio de trabajo autorizado y el destino exacto. |
| provider_payload | Discriminador de plataforma obligatorio y publish_options específicos de la plataforma. |
| scheduled_at / display_timezone | Instante ISO-8601 obligatorio con desplazamiento; zona horaria IANA de visualización válida, UTC de forma predeterminada. |
| idempotency_key | Cadena obligatoria sin espacios iniciales ni finales, de 8–160 caracteres, estable para esta intención. |
| caption / hashtags | Caption admite null y un máximo de 5,000 caracteres; predeterminado null. Hashtags tiene el valor predeterminado []; como máximo 100 cadenas de 100 caracteres. Los límites del proveedor pueden ser menores. |
| media | Predeterminado []; como máximo 20 referencias multimedia registradas. Cada una tiene kind image/video y una url canónica; incluye su asset_id listo. Las reglas de plataforma pueden ser más restrictivas. |
| link_url | URL opcional o null. El destino y el tipo de publicación deben admitirla. |
| local_scheduled_at / schedule_dst_disambiguation | Par opcional: hora local sin desplazamiento y exact/earlier/later. Proporciona ambos o ninguno; deben coincidir con el instante y la zona horaria previstos. |
| expires_at / automatic_retry_enabled | Vencimiento opcional posterior a scheduled_at; null significa sin vencimiento. El reintento automático es true de forma predeterminada y sigue sujeto a la seguridad del proveedor. |
| project_id / source_ref | UUID de proyecto opcional que admite null y objeto de procedencia del solicitante. Ninguno cambia la autoridad. |
await sosoPost("/studio-api/scheduled-posts/eligibility", {
"workspace_id": "<workspace-uuid>",
"include_quota": true,
"targets": [
{
"social_account_id": "<threads-account-uuid>",
"media": [],
"provider_payload": {
"platform": "threads",
"publish_options": {
"post_type": "TEXT"
}
},
"draft": {
"caption": "A reviewed update from our team.",
"hashtags": [],
"scheduled_at": "<future-ISO-8601-instant-with-offset>",
"display_timezone": "Asia/Hong_Kong"
}
}
]
});await sosoPost("/studio-api/scheduled-posts/create", {
"workspace_id": "<workspace-uuid>",
"social_account_id": "<threads-account-uuid>",
"caption": "A reviewed update from our team.",
"hashtags": [],
"media": [],
"provider_payload": {
"platform": "threads",
"publish_options": {
"post_type": "TEXT"
}
},
"scheduled_at": "<future-ISO-8601-instant-with-offset>",
"display_timezone": "Asia/Hong_Kong",
"idempotency_key": "<stable-key-for-this-one-intent>"
});5. Consulta el resultado y cancela de forma segura
La creación devuelve { post, deduped }. El listado devuelve { posts, next_cursor, returned_count }. La consulta devuelve { post, delivery, actions, attempts, outcomes }. Una publicación incluye id, status, version, destination, schedule e identidad/enlace permanente del proveedor cuando están disponibles; delivery y actions explican el progreso actual y los siguientes pasos permitidos.
Una publicación con status scheduled o publishing sigue pendiente. Un resultado definitivo con status published confirma la publicación e incluye la evidencia disponible del proveedor; puede faltar el enlace permanente. Si el cliente se desconecta o se retrasa una respuesta, consulta la publicación existente antes de informar del resultado.
Antes de cancelar, consulta el detalle y proporciona su versión numérica actual. El resultado puede ser cancellation_pending mientras SosoAPI cancela una programación retenida por el proveedor. Cancelar una programación no elimina una publicación ya publicada.
await sosoPost("/studio-api/scheduled-posts/get", {
"id": "<scheduled-post-uuid>"
});const detail = await sosoPost("/studio-api/scheduled-posts/get", {
id: "<scheduled-post-uuid>"
});
// Run only after the person confirms cancelling this unpublished post.
await sosoPost("/studio-api/scheduled-posts/cancel", {
id: detail.post.id, expected_version: detail.post.version
});6. Idempotencia, conflictos y escrituras inciertas
Usa el mismo idempotency_key al repetir la misma solicitud de creación. Una repetición coincidente devuelve la publicación existente con deduped=true. Cambiar el contenido reutilizando la clave provoca un conflicto. Esto evita creaciones duplicadas en SosoAPI; no garantiza una entrega exactamente una vez por parte del proveedor.
Para actualizaciones y acciones, proporciona expected_version de la respuesta de detalle más reciente. Si hay conflicto, consulta la publicación de nuevo y revisa el cambio frente a la nueva versión. Una respuesta perdida es motivo para comprobar el estado existente, no para crear otra publicación con otra clave.
La protección contra contenido duplicado puede rechazar una solicitud independiente con el mismo contenido público normalizado, cuenta y una hora cercana de publicación. Consulta la referencia de la publicación existente si tienes autorización para leerla y pregunta al usuario antes de cambiar la solicitud. Las solicitudes en lote devuelven un resultado por destino; algunos pueden tener éxito mientras otros fallan.
7. Errores, límites de frecuencia y límites de integración
Los errores del controlador usan un sobre con error, message, code opcional, retryable, request_id, details opcional y retry_after_seconds. Los errores de validación de esquema 400 devuelven otra estructura: { success: false, error: { name: "ZodError", message: "…" } }. El mensaje de validación contiene actualmente los problemas serializados de los campos, incluidas sus rutas; no supongas que están presentes los campos del sobre del controlador.
Al diagnosticar un fallo, conserva el estado HTTP, código de error e ID de correlación de result.request_id ?? response.headers.get("x-request-id"). Usa las rutas de campos de validación para corregir la solicitud. En errores de autenticación o permisos, corrige la credencial o concesión antes de volver a intentar.
El límite actual de studio-api es de 240 solicitudes por minuto por usuario humano y 480 por minuto en el cupo independiente de credenciales de máquina de ese usuario. Son límites de solicitudes, no rendimiento de publicación ni cuotas del plan. Otros endpoints y proveedores sociales tienen límites independientes. Respeta el encabezado Retry-After actual en 429 o 503 reintentable; no ejecutes un bucle de consultas continuas.
Consulta el estado a intervalos acotados mientras el usuario sigue la publicación o usa la vista de estado en vivo de la aplicación. Esta API no promete webhooks salientes, un SLA para suscriptores, funciones idénticas entre plataformas ni una hora fija de publicación.
| Estado | Significado y respuesta |
|---|---|
| 400 | La validación de esquema puede devolver success:false con un ZodError en error, en vez del sobre del controlador. Corrige sus problemas de campos; usa x-request-id si el cuerpo no tiene request_id. Las solicitudes no válidas o no admitidas a nivel de controlador también pueden usar 400. |
| 401 / 403 | Credencial ausente/vencida o acceso denegado por capacidad, rol, espacio de trabajo o cuenta. Corrige el permiso o la pertenencia exactos. |
| 404 | El recurso no está disponible en el alcance autorizado. Verifica la identidad. |
| 409 | Conflicto de estado/versión, idempotencia, contenido duplicado o elegibilidad. Vuelve a consultar; no reenvíes a ciegas. |
| 422 para bulk-create | Ningún destino tuvo éxito. Consulta failures; el éxito parcial usa 201. |
| 429 | Límite de solicitudes. Respeta Retry-After y conserva la misma identidad de intención. |
| 503 / otros 5xx | Fallo temporal del servicio o error interno. Conserva request_id; consulta el estado existente antes de reintentar una mutación incierta. |