ResumenDescripción general de la API

Resumen

Descripción general de la API

Cada endpoint de Sofia API v1 con sus parámetros, scopes y ejemplos, y un playground para probar cada uno con tu propia clave de API.

URL base https://api.sofia-post.com/api · Documento OpenAPI

Sofia API da a tu código acceso a tu cuenta de Sofia: los establecimientos de tus Workspaces, sus redes conectadas, su biblioteca multimedia, sus publicaciones y sus estadísticas. Es una API REST sobre HTTPS; las solicitudes y las respuestas son JSON. Permite:

  • Publicar: ahora, en una fecha o como borrador, en una o varias redes a la vez.
  • Comprobar antes de publicar: una simulación te dice si cada red aceptaría la publicación, sin escribir nada.
  • Gestionar los archivos: sube un archivo o impórtalo desde una URL a la biblioteca multimedia de un establecimiento.
  • Seguir las publicaciones: lee el estado de cada red, su enlace permanente o el motivo del error. Un webhook firmado también puede avisarte.
  • Leer las estadísticas: las últimas cifras recopiladas por Sofia para cada cuenta conectada.
  • Conectar redes: crea un enlace alojado que permite al propietario conectar una red que falta.

SDK e integraciones

  • CLI `sofia`: la CLI, publicada en npm con el nombre sofia-post, para manejar Sofia desde un terminal.
  • Servidor MCP: conecta Sofia a tus agentes de IA.
  • Zapier: la integración de Sofia se conecta con tu clave personal. Ofrece los disparadores «Post Published» y «Post Failed», la acción «Create Post» (publicar, programar o guardar un borrador) y la búsqueda «Find Post». Todavía no se ha enviado al directorio público de Zapier.

Autenticación

Cada llamada lleva un token Bearer en la cabecera Authorization. Sofia vuelve a leer los derechos asociados en cada llamada: una persona retirada de un Workspace, o una autorización revocada, detiene el token desde la solicitud siguiente.

Hay dos formas de autenticarse:

MétodoParaToken
Clave personalAutomatizar tu propia cuenta, scripts, agentessofia_pk_live_…, válido hasta su caducidad
OAuth, código de autorización + PKCEUn producto que actúa por sus usuarios, en el Workspace que elijansofia_live_at_…, una hora, renovable
OAuth, client credentialsUno de tus servidores que automatiza un Workspace del que eres propietariosofia_live_at_…, una hora

Clave de API

Si automatizas tu propio Workspace, basta una clave personal. En la app de Sofia, abre Ajustes › API, pestaña Acceso personal, y crea una clave. Elige su nombre, su caducidad y sus permisos. La clave solo se muestra una vez: cópiala y guárdala en una caja fuerte de secretos, nunca en tu código.

Crea una clave en Ajustes › API › Acceso personal. Actúa por ti en todos tus Workspaces: el establecimiento que nombras determina el Workspace, y tu rol en él decide lo que la clave puede hacer.

Envía la clave en la cabecera Authorization, como token Bearer:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_pk_live_…"

`GET /v1/me` te indica por quién actúa la clave, sus Workspaces y sus permisos.

Una llamada sin el permiso (el scope) necesario responde 403 insufficient_scope.

Token OAuth2

Para un producto SaaS que actúa por sus usuarios en sus propios Workspaces, registra un cliente OAuth: cada persona autoriza tu aplicación en el Workspace que elija. Los tokens de acceso OAuth empiezan por sofia_live_at_…, duran una hora y se usan del mismo modo:

curl https://api.sofia-post.com/api/v1/establishments \
  -H "Authorization: Bearer sofia_live_at_…"

¿Creas una app a la que se conectan otras cuentas de Sofia? Su flujo authorization code con PKCE, sus client credentials y sus scopes se describen en OAuth2.

URL base

Cada endpoint se encuentra bajo una única URL base:

https://api.sofia-post.com/api/v1

Límites de frecuencia

Sofia API cuenta las solicitudes en tres ventanas: 15 minutos, 24 horas y 30 días.

Token15 minutos24 horas30 días
Clave personal (por cuenta)1501.00010.000
Cliente OAuth10050015.000
Cliente OAuth, por persona autorizada (hasta 1.000)+10+25+100

Cabeceras

Cada respuesta te dice lo que queda, en formato IETF:

RateLimit: "15m";r=144;t=43, "24h";r=994;t=13543, "30d";r=9994;t=13543
RateLimit-Policy: "15m";q=150;w=900, "24h";q=1000;w=86400, "30d";q=10000;w=2592000

r es el número de solicitudes restantes, t los segundos antes de que se reinicie la ventana, q la cuota y w la ventana en segundos.

Cuando una ventana está llena

La respuesta es 429 rate_limit_exceeded, con una cabecera Retry-After y la ventana agotada en details. Espera ese plazo antes de reintentar.

Usar la API con eficacia

  • Registra un webhook en lugar de consultar una publicación en bucle.
  • Usa limit hasta 100 para leer las listas en menos páginas.
  • Guarda en caché lo que cambia poco: plataformas, establecimientos, cuentas conectadas.

Errores

Cada error responde con un estado HTTP y el mismo sobre JSON. code es el contrato que lee tu código; message está escrito para una persona y puede cambiar.

{
  "error": {
    "code": "invalid_request",
    "message": "The request is invalid",
    "details": [
      { "field": "targets[0].text", "code": "required", "message": "targets[0].text is required" }
    ],
    "docUrl": "https://www.sofia-post.com/developers/errors#invalid_request",
    "requestId": "2aab5943-6333-4caf-8478-25a85ebd4abf"
  }
}
  • details dice más: el campo rechazado, el motivo de una red, la ventana de límite agotada.
  • docUrl lleva a la explicación del código en esta página.
  • requestId identifica la solicitud: comunícaselo al soporte.

Códigos de error

EstadoCódigoSignificado
400invalid_requestEl cuerpo, la consulta o un parámetro no es válido. Un detalle por campo rechazado: field, code (required, is_string, max…) y message.
400invalid_jsonEl cuerpo no es JSON válido.
400invalid_cursorEl cursor no lo devolvió Sofia API.
400webhook_url_invalidLa dirección del webhook no es https, o lleva a una dirección privada.
400webhook_event_invalidEvento de webhook desconocido.
400application_name_invalidAjustes › API: falta el nombre del cliente o es demasiado largo.
400application_profile_invalidAjustes › API: un campo del perfil del cliente no es válido; el detalle lo indica.
400logo_invalidAjustes › API: el logotipo se rechaza (mínimo 180×180 px, PNG, JPEG o WebP, 2 MB como máximo).
401invalid_tokenEl token falta, es desconocido, ha caducado o está revocado, o la persona que hay detrás perdió los derechos que lleva. Consulta la cabecera WWW-Authenticate.
401unauthorizedSe requiere autenticación.
402subscription_requiredUna escritura real (simulación incluida) exige una suscripción activa o una prueba.
403insufficient_scopeAl token le falta el scope del endpoint; el detalle lo nombra.
403insufficient_roleTu rol en este Workspace no permite esta acción.
403live_mode_requiredUna escritura real con un token de prueba.
403route_closed_to_applicationsEste endpoint no está abierto a aplicaciones.
403forbiddenAcceso denegado.
403session_requiredAjustes › API: esta acción requiere una sesión de Sofia, no un token.
403impersonation_forbiddenAjustes › API: esta acción se rechaza en una sesión prestada.
403workspace_owner_requiredSolo el propietario del Workspace puede hacerlo.
403workspace_member_requiredNo eres miembro de este Workspace.
404route_not_foundNingún endpoint responde a este método y esta ruta.
404establishment_not_foundEstablecimiento desconocido, o fuera del alcance del token.
404media_not_foundMedio desconocido, o fuera del alcance del token.
404post_not_foundPublicación desconocida, o fuera del alcance del token.
404webhook_not_foundWebhook desconocido, o de otra aplicación.
404not_foundEl recurso no existe.
404application_not_foundAjustes › API: cliente desconocido.
404grant_not_foundAutorización desconocida o ya revocada.
404personal_key_not_foundAjustes › API: clave personal desconocida.
405method_not_allowedEste método no está permitido en este endpoint.
409post_in_flightUna red está enviando la publicación: reintenta la eliminación un momento después.
409post_not_editableUna red ya envió la publicación: ya no se puede editar.
409account_already_connectedEsta red ya está conectada al establecimiento.
409webhook_limit_reachedLa aplicación ya tiene 10 webhooks.
409idempotency_in_progressLa primera petición con esta Idempotency-Key sigue en curso.
409conflictLa solicitud entra en conflicto con el estado actual del recurso.
409scope_removal_forbiddenAjustes › API: a un cliente se le puede añadir un permiso, pero no quitárselo.
409public_client_has_no_secretAjustes › API: un cliente público no tiene secreto.
413payload_too_largeEl archivo supera el límite de subida.
415unsupported_media_typeTipo de contenido no admitido.
422post_refusedUna red, el establecimiento o tu plan rechazaría la publicación. Un detalle por motivo, cada uno con su message.
422media_import_refusedNo se pudo importar el medio desde esta URL; el detalle da el motivo.
422media_upload_refusedEl archivo subido no es una imagen o un vídeo admitido.
422platform_not_offeredEsta red no se ofrece para conectar.
422idempotency_key_reusedEsta Idempotency-Key se usó con un cuerpo distinto.
422unprocessable_entityLa solicitud se entiende, pero no se puede procesar.
429rate_limit_exceededUna ventana de límite está llena: espera el tiempo de Retry-After.

Motivos de una publicación rechazada

Un post_refused lista cada motivo en details[].code:

CódigoSignificado
unsupported_platformEsta red no es compatible.
duplicate_platformLa misma red aparece dos veces.
unsupported_formatEsta red no ofrece este formato.
caption_too_longEl texto es demasiado largo (max, actual).
too_many_hashtagsDemasiados hashtags (max, actual).
too_many_mentionsDemasiadas menciones (max, actual).
too_many_thread_postsEl hilo es demasiado largo (max, actual).
too_many_mediaDemasiados medios (max, actual).
media_mix_refusedNo se pueden mezclar fotos y vídeos en esta red.
media_requiredEsta red necesita una foto o un vídeo.
media_type_refusedEsta red no acepta este tipo de medio.
scheduled_in_pastscheduledAt está en el pasado.
scheduled_at_invalidUna publicación programada necesita un scheduledAt futuro.
account_not_connectedNo hay ninguna cuenta conectada en esta red para este establecimiento.
account_ambiguousHay varias cuentas conectadas en esta red: indica una con accountId.
media_not_foundEl medio no está en la biblioteca de este establecimiento.
media_url_invalidLa URL del medio debe ser una dirección https pública.
platform_unavailableEsta red todavía no está abierta en Sofia.
publication_quota_exceededSe alcanzó la cuota de publicaciones de tu plan.

Gestionar los errores

  • Basa tu lógica en code, nunca en message.
  • Reintenta un 429 después de Retry-After, y un 5xx con un backoff exponencial.
  • No reintentes nunca un 4xx idéntico: corrige primero la solicitud.
  • Registra requestId en cada fallo.

Paginación

Las listas (GET /v1/posts, GET /v1/media) se devuelven con las más recientes primero, una página cada vez. limit fija el tamaño de página, de 1 a 100 (20 por defecto).

{
  "data": [ … ],
  "nextCursor": "WyIyMDI2LTEwLTI3VDE1OjExOjU5LjAxN1oiLCIx…"
}

Pasa nextCursor como cursor para leer la página siguiente. Cuando nextCursor no aparece, has llegado al final.

let cursor;
do {
  const url = new URL('https://api.sofia-post.com/api/v1/posts');
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);
  const page = await globalThis.fetch(url, { headers: { Authorization: `Bearer ${key}` } }).then((r) => r.json());
  handle(page.data);
  cursor = page.nextCursor;
} while (cursor);

Un cursor es opaco: nunca lo construyas ni lo modifiques. Un cursor que Sofia no ha devuelto responde 400 invalid_cursor.

Idempotencia

Un error de red puede dejarte con la duda sobre la creación de una publicación. Envía una cabecera Idempotency-Key con POST /v1/posts y POST /v1/media, y reintenta con la misma clave: Sofia responde lo que respondió la primera vez, y no crea nada por duplicado.

curl -X POST https://api.sofia-post.com/api/v1/posts \
  -H "Authorization: Bearer $SOFIA_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -d @post.json
  • Usa una clave aleatoria nueva (un UUID) para cada publicación nueva, y la misma clave para cada nuevo intento de esa publicación.
  • La misma clave con un cuerpo distinto responde 422 idempotency_key_reused.
  • Mientras la primera solicitud está en curso, la misma clave responde 409 idempotency_in_progress: reintenta un momento después.

Modo de prueba y simulación

Simulación. Añade ?dry_run=true a POST /v1/posts: Sofia ejecuta las comprobaciones de una publicación real (las reglas de cada red, la cuenta conectada, los archivos, la fecha y tu plan) sin guardar ni enviar nada. El detalle de las respuestas está en la página Publicar.

Modo de prueba. Una aplicación creada en modo test (tokens sofia_test_at_…) lee tu Workspace real pero nunca escribe en él: sus publicaciones se simulan y se juzgan con las mismas reglas, y toda escritura real responde 403 live_mode_required. Las claves y los clientes creados en Ajustes › API están en modo live; usa la simulación para comprobar sin publicar.

OAuth

Cuenta

Plataformas

Establecimientos

Medios

Publicaciones

Webhooks