Saltar al contenido principal

Cumplimiento

El registro de cumplimiento es una entrada por todo lo que ocurrió con las publicaciones de un espacio de trabajo una vez que lo abandonaron: cada publicación entregada, cada actualización, eliminación y reenvío, cada decisión de aprobación, el resultado de cada entrega, cada informe de en qué punto está una publicación y cada cambio en la aprobación de un canal. Cada entrada se escribe una vez y nunca se modifica, se sella en la cadena del espacio de trabajo, la firma Reelwire, una entidad externa le pone un sello de tiempo una vez al día y se conserva diez años, ocurra lo que ocurra desde entonces con el clip, la publicación o el canal. Para qué sirve y cómo lo usa una persona se explica en la página de la pantalla Cumplimiento.

El registro forma parte de Enterprise y registra desde el momento en que un espacio de trabajo está en él. Un espacio de trabajo que deja Enterprise no registra nada nuevo y sigue leyendo y exportando lo que tiene durante 90 días; después se elimina. En cualquier otro plan, leerlo es un 403 con una frase que lo dice. Cambiar las reglas de aprobación y añadir auditores es exclusivo de Enterprise, también un 403 durante esos 90 días; eliminar un auditor funciona en cualquier plan.

MétodoRutaPermiso
GET/v1/compliancecompliance:read
GET/v1/compliance/recordscompliance:read
GET/v1/compliance/records/{id}compliance:read
GET/v1/compliance/exportcompliance:read
GET/v1/compliance/export/linkcompliance:read
GET/v1/compliance-files/{tenantId}solo el enlace firmado, sin credencial
GET/v1/compliance/auditcompliance:read
PUT/v1/compliance/approval-rules/{channelId}persona con sesión iniciada: Aprobaciones
GET/v1/compliance/auditorspersona con sesión iniciada
POST/v1/compliance/auditorspersona con sesión iniciada: Administración
POST/v1/compliance/auditors/{userId}/resendpersona con sesión iniciada: Administración
POST/v1/compliance/auditors/{userId}/deletepersona con sesión iniciada: Administración

Quién lo lee. Una clave con el permiso Cumplimiento, que es de solo lectura: una máquina puede leer el registro, comprobar un archivo con él y obtener la exportación para un archivo propio. Una persona con sesión iniciada con Aprobaciones o Administración (el propietario tiene ambos) y un auditor. Las reglas de aprobación y los auditores responden a una sesión del panel y nunca a una clave, porque unas aflojan un control y los otros dejan entrar a alguien: una clave recibe el rechazo «Sign in to do this» descrito en Autenticación.

El resumen​

curl http://localhost:4000/v1/compliance \
-H "Authorization: Bearer rw_your_key_here"
{
"on": true,
"lapsed": null,
"retentionYears": 10,
"graceDays": 90,
"entries": 1284,
"sealed": 1283,
"waiting": 1,
"firstEntryAt": "2026-10-14T09:12:40.118Z",
"chain": { "seq": 1283, "headHash": "4f0c9a...e71d" },
"anchor": {
"day": "2026-10-15",
"seq": 1270,
"state": "STAMPED",
"genTime": "2026-10-15T00:01:12.000Z",
"tsaUrl": "http://timestamp.digicert.com"
},
"timeStampAuthority": "http://timestamp.digicert.com",
"keys": [
{ "id": "01M3B4Z9Q8R0J5V2C6N1T7K4WD", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...", "createdAt": "2026-10-14T09:12:41.002Z", "retired": false }
],
"kinds": [
{ "id": "post.publish", "label": "Handed over" },
{ "id": "post.update", "label": "Update handed over" }
]
}

Abreviado: kinds lista los ocho, y los hashes tienen 64 caracteres hexadecimales.

  • on indica si el espacio de trabajo registra ahora. lapsed es null o, para un espacio de trabajo que dejó Enterprise, since (cuándo) y deletesOn (cuándo se elimina lo que registró).
  • entries las cuenta, sealed las que tienen su número y su firma, y waiting las que se sellarán en los próximos segundos.
  • chain es el número y el hash de la entrada más reciente.
  • anchor es el sello de tiempo diario más reciente que cubre este espacio de trabajo, hasta qué entrada (seq), y su state: PENDING mientras se vuelve a preguntar a la entidad, cada diez minutos aproximadamente, STAMPED con el momento en que firmó (genTime), o FAILED cuando se le ha preguntado 48 veces sin respuesta.
  • keys son las mitades públicas de las claves con las que Reelwire firma las entradas, en PEM.

Entradas​

curl "http://localhost:4000/v1/compliance/records?kind=post.publish&perPage=1" \
-H "Authorization: Bearer rw_your_key_here"
{
"records": [
{
"id": "01M3B5C2D1E0F9G8H7J6K5M4N3",
"kind": "post.publish",
"label": "Handed over",
"occurredAt": "2026-10-15T08:30:02.411Z",
"summary": "Orchid Invest DE: Der DAX schloss 412 Punkte hoeher.",
"tone": "good",
"channels": ["Orchid Invest DE"],
"channelIds": ["01M1VFRWE7EWSY5D7QDQ0KNK1S"],
"postKey": "01M3B5BZQ1X0W9V8T7S6R5Q4P3",
"postIds": ["01M3B5C0A9B8C7D6E5F4G3H2J1"],
"videoSha256": ["9b2e1f...c04a"],
"seq": 1283,
"sealed": true,
"entryHash": "4f0c9a...e71d",
"frames": 3,
"stills": "DONE"
}
],
"total": 412,
"all": 1284,
"page": 1,
"perPage": 1,
"facets": { "kind": ["post.publish", "post.update", "..."], "channelId": ["01M1VFRWE7EWSY5D7QDQ0KNK1S"] },
"labels": { "kind": { "post.publish": "Handed over" }, "channelId": { "01M1VFRWE7EWSY5D7QDQ0KNK1S": "Orchid Invest DE" } }
}

Se pagina, busca y ordena como todo registro (consulte Listas y paginación), las más recientes primero salvo que ascending=true:

ParámetroQué hace
since, untilSolo las entradas ocurridas en esta ventana, instantes ISO 8601.
kindUn tipo o varios, separados por comas (abajo).
channelIdUn canal o varios, separados por comas.
sha256Solo las entradas que nombran este archivo de video, por los 64 caracteres hexadecimales de su SHA-256. Cualquier otra cosa es un 400. Consulte Comprobar un archivo de video.
qUna clave de publicación, un id de publicación, el hash de una entrada o palabras del texto de una publicación o del nombre de un canal.
page, perPageperPage es de 1 a 200, 25 por defecto.

summary es la entrada en una línea, leída solo de la entrada, y tone cómo se lee de un vistazo (good, bad, waiting o plain). sealed es false durante los pocos segundos antes de que una entrada tenga su número (seq) y su hash. stills indica en qué punto están sus fotogramas: NONE para una entrada sin video propio, PENDING hasta que se toman, DONE o MISSING cuando el clip había salido antes del almacenamiento.

kindEtiquetaSe escribe cuando
post.publishEntregadaUna publicación salió por un canal. Una entrada por canal de una entrega.
post.updateActualización entregadaSe entregó un cambio en una publicación ya entregada.
post.deleteEliminación entregadaSe entregó una solicitud para retirar una publicación.
post.resendEnviada de nuevoUn reenvío por correo electrónico, por webhook o a los participantes de sindicación, una republicación a través del conector de plataforma, o un reintento de una publicación que falló.
approval.decisionDecisión de aprobaciónAlguien aprobó o rechazó una publicación o una actualización, o dio la primera de dos aprobaciones.
delivery.outcomeResultado de la entregaUn correo, una llamada de webhook o una publicación del conector de plataforma se entregó, o falló tras todos los intentos.
post.reportInformadaSe informó de que una publicación se publicó (con su enlace), se omitió, no se publicó o se eliminó.
channel.approvalRegla de aprobación cambiadaSe cambió la aprobación de un canal, o cambió su número de aprobadores.

Una entrada​

GET /v1/compliance/records/{id} responde con una entrada completa: lo que se registró, su sello, el sello de tiempo diario que la cubre y sus fotogramas.

{
"record": {
"id": "01M3B5C2D1E0F9G8H7J6K5M4N3",
"kind": "post.publish",
"label": "Handed over",
"occurredAt": "2026-10-15T08:30:02.411Z",
"summary": "Orchid Invest DE: Der DAX schloss 412 Punkte hoeher.",
"body": {
"v": 1,
"kind": "post.publish",
"occurredAt": "2026-10-15T08:30:02.411Z",
"workspace": { "id": "01M1VFRWE3A35H892VAAEKKDA6", "name": "Orchid Invest" },
"handover": { "id": "01M3B5C1...", "postKey": "01M3B5BZQ1X0W9V8T7S6R5Q4P3", "ways": ["EMAIL", "WEBHOOK"] },
"post": { "id": "01M3B5C0A9B8C7D6E5F4G3H2J1", "postGroupId": "01M3B5BZW..." },
"channel": { "id": "01M1VFRWE7EWSY5D7QDQ0KNK1S", "name": "Orchid Invest DE", "platform": "instagram", "surface": "instagram-reels", "ratio": "9x16", "locale": "de" },
"brand": "Orchid Invest",
"template": { "id": "tpl_dd7e6b264fb8", "variant": "Corporate glass", "version": "1.0.0", "build": "a41c...9e02" },
"source": { "kind": "queue", "queueName": "Market close", "title": "DAX close" },
"author": { "kind": "person", "id": "anna@orchid.example", "name": "Anna Berg" },
"ai": null,
"approval": {
"required": true,
"needed": 1,
"decisions": [{ "by": { "kind": "person", "id": "lena@orchid.example", "name": "Lena Kraus" }, "at": "2026-10-15T08:29:51.004Z", "note": "Figures checked." }]
},
"video": { "assetId": "01M3B5BZX...", "kind": "video", "ratio": "9x16", "mimeType": "video/mp4", "width": 1080, "height": 1920, "durationMs": 12000, "byteSize": 3481202, "sha256": "9b2e1f...c04a" },
"text": { "caption": "Der DAX schloss 412 Punkte hoeher.", "hashtags": ["dax", "boerse"], "disclaimer": "Keine Anlageberatung." }
},
"bodyHash": "c3d1...77aa",
"seal": {
"seq": 1283,
"prevHash": "8e91...02bd",
"entryHash": "4f0c9a...e71d",
"signature": "3vQ9...Bw==",
"keyId": "01M3B4Z9Q8R0J5V2C6N1T7K4WD",
"sealedAt": "2026-10-15T08:30:03.120Z"
},
"anchor": null,
"stills": "DONE",
"frames": [
{ "id": "01M3B5D...", "atMs": 1200, "width": 480, "height": 854, "url": "http://localhost:4000/v1/assets/01M3B5D.../download?exp=...&sig=...", "expiresAt": "2026-10-15T09:31:00.000Z" }
]
}
}

Abreviado: los tres fotogramas se toman a una décima parte, a la mitad y a nueve décimas partes del video, y sus enlaces funcionan durante una hora. anchor es null hasta que el siguiente sello de tiempo diario cubre la entrada y entonces es su day, state, genTime, tsaUrl y root. Un id que no es de una entrada de este espacio de trabajo es un 404.

Lo que lleva body depende de su tipo, y cada cuerpo indica su v, kind y occurredAt:

  • Una entrega (post.publish, post.resend): el espacio de trabajo, la entrega con sus ways (EMAIL, WEBHOOK, CONNECTOR, SYNDICATION) y, en un reenvío, again, que indica cuál; la publicación (con permalink donde se conocía uno), el canal, la marca, la plantilla con la huella de su build, el source, el author (una persona, una clave de API o feed para un feed predefinido), ai (lo que hicieron los pasos de IA del servidor de feeds con el contenido de un feed predefinido, tal como lo indicó al enviarlo), la approval con cada decisión, el video y la cover con su SHA-256, el text, cada longitud del texto en que se entregó (captions) y los ajustes de publicación. Un reenvío nombra quién lo pidió (requestedBy).
  • Una actualización: el mismo canal y publicación, requestedBy, la approval por la que pasó el cambio y el change en sí: su texto, sus textos y, donde sustituyó el video, el nuevo video.
  • Una eliminación: el canal y la publicación, y requestedBy.
  • Una decisión de aprobación: approval con about (post o update), decision (approved, rejected o first-approval), needed, by, note, requestedBy (el autor) y, para una actualización, el change aprobado.
  • El resultado de una entrega: delivery con su way, outcome (delivered o failed), attempts, el target del webhook o de la plataforma, el último status y una note.
  • Un informe: report con el state de la publicación (PUBLISHED, SKIPPED, FAILED o RECALLED), by y la url, platformPostId o reason indicados.
  • Una regla de aprobación cambiada: el canal, by, y from y to, cada uno con su approval y approvers.

Las personas se nombran por valor tal como eran en ese momento (kind, id como dirección de inicio de sesión o id de la clave, y name), de modo que un colega eliminado o una clave revocada desde entonces sigue nombrando quién lo hizo. El texto que vino de fuera del espacio de trabajo, el error de un receptor o un motivo que alguien escribió en un informe, se registra tal como llegó.

Comprobar un archivo de video​

¿Qué publicación es este archivo? Calcule su SHA-256 donde está y pregunte:

sha256sum clip.mp4          # macOS: shasum -a 256 clip.mp4
curl "http://localhost:4000/v1/compliance/records?sha256=9b2e1f...c04a" \
-H "Authorization: Bearer rw_your_key_here"

Vuelve cada entrada que nombra ese archivo. Una coincidencia demuestra, hasta el byte, que es el archivo entregado. Una copia descargada de una plataforma nunca coincide, porque las plataformas recodifican lo que publican: la huella es del archivo que Reelwire entregó, el que recibió su receptor o su kit de publicación, que es el que hay que conservar.

La exportación​

GET /v1/compliance/export responde con un ZIP para un período y, cuando se pide, algunos canales: lo que se entrega a un auditor. Es lo que descarga Descargar exportación para un auditor en la pantalla.

curl -o compliance.zip \
"http://localhost:4000/v1/compliance/export?since=2026-09-01T00:00:00Z&until=2026-09-30T23:59:59Z&channelId=01M1VFRWE7EWSY5D7QDQ0KNK1S" \
-H "Authorization: Bearer rw_your_key_here"
ParámetroQué hace
since, untilEl período, instantes ISO 8601. until es por defecto ahora y since 30 días antes. Un período que empieza después de terminar es un 400.
channelIdSolo algunos canales, separados por comas o repetidos. Si falta, son todos los canales.

Responde con application/zip, con el nombre de archivo del espacio de trabajo y del período. Solo contiene entradas selladas; una escrita en los últimos segundos irá en la siguiente. Una exportación contiene como máximo 3000 entradas: un período con más es un 409 que indica cuántas contiene, así que pida un período más corto o menos canales y obtenga el resto por separado. Cada exportación se escribe en el registro de auditoría (compliance.exported), con el período y los canales.

ArchivoQué es
report.pdfEl registro dispuesto para una persona, publicación por publicación, con tres fotogramas de cada video. Su primera página indica lo que encontró Reelwire al comprobar la exportación antes de escribirla. Describe; records.json demuestra.
records.jsonentries, cada una con su body, bodyHash, seq, prevHash, entryHash, signature y keyId; chain, cada eslabón desde la primera entrada del período hasta la más reciente que cubre un sello de tiempo diario; anchors, cada sello de tiempo diario con la prueba de este espacio de trabajo; y keys, las claves públicas que firmaron.
anchors/Cada sello de tiempo diario (RFC 3161), tal como lo respondió la entidad, un archivo .tsr por día.
stills/Los fotogramas que muestra el informe, nombrados por entrada y posición.
verify.mjsCada comprobación de abajo, escrita. Node.js 18 o superior, nada que instalar.
README.txtQué es todo ello y cómo comprobarlo.

GET /v1/compliance/export/link, con los mismos since, until y channelId, responde con una dirección firmada a ese ZIP en lugar del ZIP: para quien llama y no puede llevar el archivo a la persona que lo quiere, como un asistente de IA en una conexión alojada, que da a la persona el enlace para pulsar.

{
"url": "https://app.reelwire.io/v1/compliance-files/01M1V...?since=2026-09-01T00%3A00%3A00.000Z&until=...&link=01M...&exp=1792371234&sig=...",
"expiresAt": "2026-10-18T21:15:00.000Z",
"since": "2026-09-01T00:00:00.000Z",
"until": "2026-09-30T23:59:59.000Z",
"channels": []
}

La dirección no necesita credencial, porque es una, para esta exportación solamente, y funciona quince minutos. El período, los canales y el espacio de trabajo van firmados en ella: una dirección editada para llegar más atrás o a otro espacio de trabajo es un 403, y también lo es una que ha pasado su tiempo. El espacio de trabajo aún debe tener el registro cuando se usa el enlace. Pedir un enlace queda en el registro de auditoría (compliance.export_linked), y también cada descarga a través de él (compliance.exported, a nombre de quien pidió el enlace).

Verificar una exportación​

Nada en ella pregunta a Reelwire. Desde la carpeta en la que se descomprimió:

node verify.mjs                     # every entry, the chain and the time stamps
node verify.mjs --clips ./videos # also: which entry each video in ./videos is

Imprime una línea por hallazgo, termina con «Everything checked holds.» o con cuántas comprobaciones fallaron, y sale con 1 cuando algo falló. Lo que comprueba, para quien escriba la suya:

  1. El contenido de cada entrada. bodyHash es el SHA-256, en hexadecimal, de body como JSON canónico: claves ordenadas en todos los niveles, nada indefinido, sin espacios.

  2. El sello de cada entrada. entryHash es el SHA-256 del JSON canónico de un objeto que contiene el id del espacio de trabajo (tenantId, workspace.id en records.json), seq, prevHash y bodyHash. signature es la firma Ed25519 de Reelwire, en base64, sobre los 32 bytes de entryHash, comprobada con la clave de keys que nombra keyId.

  3. La cadena. Los números se suceden sin huecos, y el prevHash de cada eslabón es el entryHash del anterior. La primera entrada que un espacio de trabajo registró enlaza con 64 ceros. Una entrada cambiada, eliminada o intercalada después rompe todos los eslabones posteriores.

  4. Los sellos de tiempo diarios. Una vez por día UTC, la entrada más reciente de cada espacio de trabajo que registró algo se convierte en una hoja de un árbol de Merkle, y una entidad externa de sellado de tiempo firma su raíz. Una hoja es el SHA-256 de un byte 0 seguido de tenantId, seq y headHash unidos por saltos de línea; un nodo es el SHA-256 de un byte 1 seguido de los 32 bytes de cada uno de los dos hashes. El proof.path de cada ancla lista los hashes hermanos desde la hoja hacia arriba, con el lado (L o R) en que está cada uno, y lleva a root. Una prueba nombra solo hashes, nunca otro espacio de trabajo.

  5. La firma propia de la entidad, con OpenSSL. El root del ancla es el hash que firmó la entidad, así que se da como resumen, con un paquete de CA que contenga el certificado raíz de la entidad (el paquete propio de la mayoría de los sistemas lo tiene):

    openssl ts -verify -digest <root> -in anchors/<day>.tsr -CAfile <CA bundle>
    openssl ts -reply -in anchors/<day>.tsr -text # what a time stamp says

    La entidad figura en cada ancla (tsaUrl) y publica sus propios certificados.

Una entrada que lo supera fue escrita por Reelwire y no ha cambiado desde entonces. Una entrada que cubre un sello de tiempo existía, tal como es, en el momento en que firmó la entidad: nadie, Reelwire incluido, pudo escribirla o cambiarla después sin romper la cadena que lleva a ese sello. Las entradas posteriores al último sello de tiempo están selladas y firmadas, y esperan al siguiente.

El registro de auditoría​

GET /v1/compliance/audit es el registro de auditoría propio del espacio de trabajo: quién cambió qué, a quién se añadió y con qué permisos, qué claves se crearon, por qué pasó cada publicación.

{
"entries": [
{
"id": "01M3B5E...",
"occurredAt": "2026-10-15T08:29:51.010Z",
"actor": "lena@orchid.example",
"actorKind": "person",
"action": "approval.decided",
"entityType": "ApprovalRequest",
"entityId": "01M3B5D7...",
"fromState": "PENDING",
"toState": "APPROVED",
"detail": { "kind": "POST", "posts": 1, "note": "Figures checked." }
}
],
"total": 37,
"all": 9120,
"page": 1,
"perPage": 25,
"facets": { "who": ["all"] }
}

Admite since, until, q (una acción, una persona o clave, o un id), page, perPage y ascending, como las entradas. Por defecto lista lo que hizo una persona o una clave (actorKind person o key); who=all añade los pasos propios de Reelwire con cada publicación (system), que son la mayor parte.

Reglas de aprobación​

En Enterprise, un canal que pide aprobación puede pedir dos aprobadores: dos personas distintas con Aprobaciones aprueban cada una una publicación, o una actualización, antes de que salga. Si un canal pide aprobación es su approval en Canales; approvers ahí indica el número, y esto lo establece.

curl -X PUT http://localhost:4000/v1/compliance/approval-rules/01M1VFRWE7EWSY5D7QDQ0KNK1S \
-H "Authorization: Bearer rws_your_session" \
-H "Content-Type: application/json" \
-d '{"approvers":2}'
{ "channel": { "id": "01M1VFRWE7EWSY5D7QDQ0KNK1S", "name": "Orchid Invest DE", "approval": "PER_POST", "approvers": 2 } }

approvers es 1 o 2; cualquier otra cosa es un 400, y un canal que no es de este espacio de trabajo o que se eliminó, un 404. Necesita una persona con Aprobaciones, ya que reducirlo afloja un control. Se aplica a lo que empieza a esperar desde entonces; lo que ya espera conserva el número con el que se pidió. Un cambio se escribe en el registro de auditoría y es una entrada del registro.

Otra regla se cumple en Enterprise sin ajuste: nadie aprueba su propia publicación ni actualización. Consulte Aprobaciones para ver cómo aparecen ambas en /v1/approvals.

Auditores​

Personas ajenas al espacio de trabajo que inician sesión para leer el registro, su exportación, el registro de auditoría y, sin cambiar nada, el resto del espacio de trabajo. No ocupan ningún usuario, no están en GET /v1/team/users, y un espacio de trabajo tiene como máximo 5. Quedan bloqueados mientras el espacio de trabajo no esté en Enterprise.

GET /v1/compliance/auditors responde con auditors, cada uno con id, email, displayName, lastLoginAt y createdAt, y max.

POST /v1/compliance/auditors con { "email": "auditor@example.com", "displayName": "Jane Doe" } (el nombre opcional, hasta 120 caracteres) añade uno y le envía el correo que recibe un colega, con un enlace para establecer su propia contraseña que no caduca. Responde 201 con el auditor y una password de un solo uso, que se muestra solo en esta respuesta. Un sexto auditor, o una dirección ya usada en cualquier parte de Reelwire, es un 409.

POST /v1/compliance/auditors/{userId}/resend envía un enlace nuevo, que sustituye al anterior; dentro del minuto siguiente al último es un 409. POST /v1/compliance/auditors/{userId}/delete cierra sus sesiones en todas partes, elimina su cuenta, se lo comunica por correo y responde { "removed": true }; lo que hizo queda en el registro de auditoría. Eliminar funciona en cualquier plan, de modo que un espacio de trabajo que dejó Enterprise todavía puede dar de baja a uno. Las rutas propias del equipo rechazan a un auditor con un 409 que apunta aquí.