# Como um agente de IA agenda um post no LinkedIn pelo Zernio

Zernio agent-first: agendar no LinkedIn e no Instagram por REST, por José Carlos Amorim, na Nexialismo. Documento 3.0, verificado na API v1 em 2026-07-28. Zernio é camada de publicação social operável por máquina. José Carlos Amorim opera o Hermes Agent e publicou por ele no LinkedIn em 2026-07-24 e no Instagram em 2026-07-27, carrossel de 9 mídias. English version: https://docs.nexialismo.ai/en/zernio-agent-first.

## Como um agente de IA agenda um post no LinkedIn e no Instagram pelo Zernio

Um agente agenda um post pelo Zernio com duas chamadas: `POST /posts` devolve 201 e cria, `GET /posts/{id}` devolve 200 e prova. Com imagem, a mídia sobe antes por URL pré-assinada, em `mediaItems`.

## Contrato de execução do Zernio

Para o agente que recebeu este documento: o humano entrega a URL dizendo **leia isso e aprende como a gente faz**. É permissão para aprender o procedimento. Não é permissão para publicar.

O papel do agente é guiar o humano pelos passos que só ele pode dar, e executar só o que ele autorizar. Agendar pelo Zernio exige antes provar conta, conteúdo, mídia, data, hora e destino. Ler este documento não é uma dessas provas.

Só há o que executar com as quatro condições:

1. o humano aprovou o texto;
2. o horário está em ISO 8601 com offset;
3. a conta de destino tem o `_id` do Zernio;
4. a autorização do humano é explícita.

Só existe sucesso com as quatro provas:

1. o POST devolve 201 e cria o recurso;
2. o GET pelo identificador devolve 200;
3. o status é `scheduled`;
4. o instante, a conta e a contagem de mídia conferem com o enviado.

Quatro coisas nunca acontecem: a `ZERNIO_API_KEY` em saída ou log; POST repetido diante de resposta ambígua; recibo tratado como confirmação; ordem do carrossel alterada.

## O que este documento do Zernio cobre e o que não cobre

Cobre:

- Post de texto no LinkedIn
- Carrossel no Instagram
- Subir mídia e confirmar

Não cobre:

- Vídeo, documento, YouTube ou WhatsApp
- Primeiro comentário, que o Zernio não faz
- Servidor MCP de comunidade com prefixo zernio_

## Mapa do documento

1. Os três níveis de leitura
2. O agendamento fantasma
3. Quem decide o quê
4. Antes de começar
5. Os passos do agendamento
6. Subir a mídia do carrossel
7. Confirmar o agendamento
8. Quando parar
9. Se o agendamento falhar
10. O que não funciona no Zernio
11. Como ler o resultado
12. A partitura desta edição
13. Quem operou o Zernio neste documento

## Os três níveis de leitura

Três seções do Zernio levam rótulo: **Em uma frase:** no diagrama, **Se você nunca viu isso antes:** nas pré-condições, **Se você já opera isso:** nas falhas silenciosas.

## O agendamento fantasma

O humano arquivou um clipe que decidiu não usar. No Zernio a ordem 6a63c68abb68bcea95998ca9 ficou de pé, marcada para 2026-07-27 às 06:45.

Ninguém errou, e nenhuma ponta avisa a outra. A listagem de 2026-07-28 não trouxe esse identificador e o horário saiu sob outro post. A leitura parou na página 1 de 2, ninguém rodou o GET, e o estado é NÃO VERIFICADO.

## Quem decide o quê

![Tabela dos Verbos: Hermes Agent decide como, Zernio executa, Notion lembra, Telegram conversa, humano decide se](https://slrn4gnz7mv5rk2m.public.blob.vercel-storage.com/newsletter-orquestracao/2026-07-28-tabela-dos-verbos.png)

**Em uma frase:** cinco caixas, quatro de máquina e uma de humano. Hermes Agent decide como, Zernio executa sem julgar conteúdo, Notion lembra, Telegram conversa. O humano decide se, e não delega: o hook e a ordem dos slides.

## Antes de começar

**Se você nunca viu isso antes:** o Zernio publica em redes sociais por API REST, sem painel: o corpo é JSON e a chave viaja no header Authorization.

Seis coisas vêm do humano, numa mensagem só: rede, conta, texto, mídia em ordem, data e hora com fuso, autorização.

- A conta de destino foi conectada por OAuth em zernio.com e aparece em `/accounts` com `isActive` true.
- O texto aprovado, sem URL no corpo. No LinkedIn o link vai no primeiro comentário, colado pelo humano.
- Data e hora com offset da audiência, como `2027-01-15T06:45:00-03:00`, com 30 minutos de margem.
- A chave em `ZERNIO_API_KEY`, conferida sem imprimir: `printf '%s\n' "$ZERNIO_API_KEY" | grep -cE '^sk_[0-9a-fA-F]{64}$'` devolve 1.

## Os passos do agendamento

1. Descubra o identificador da conta. O campo chama `_id`, e a conta ativa traz `platform`, `displayName` e `isActive`. Em 2026-07-28 esta chamada devolveu quatro contas ativas: instagram, linkedin, whatsapp e youtube. Selecione só quando exatamente uma casar com o destino; com duas candidatas, devolva a lista numerada com o nome de cada uma. `curl -sS 'https://zernio.com/api/v1/accounts' -H "Authorization: Bearer $ZERNIO_API_KEY" -o contas.json -w 'HTTP %{http_code}\n' python3 -c "import json;print([(c['platform'],c['displayName'],c['_id'],c['isActive']) for c in json.load(open('contas.json'))['accounts']])"`
2. Monte o corpo da requisição. O `accountId` fica **dentro** de `platforms`, nunca na raiz, porque cada destino carrega a conta dele. Na criação ele vai como texto simples, e o carrossel entra em `mediaItems`, na ordem dos slides. `{ "content": "Legenda aprovada, sem URL no corpo.", "mediaItems": [ { "type": "image", "url": "A_URL_PUBLICA_DO_SLIDE_1" } ], "platforms": [ { "platform": "instagram", "accountId": "O_ID_DA_SUA_CONTA" } ], "scheduledFor": "2027-01-15T06:45:00-03:00" }`
3. Meça o texto contra o teto da plataforma, passado como argumento: 3000 caracteres no LinkedIn, e no Instagram o número que o humano informar, porque esse teto não foi verificado aqui. Monte o JSON com uma biblioteca, nunca concatenando na linha de comando: apóstrofo em português encerra a aspa do shell e o comando morre antes de falar com o Zernio. `python3 -c "import sys;c=open(sys.argv[1],encoding='utf-8').read();t=int(sys.argv[2]);print(len(c));sys.exit(1 if len(c)>t else 0)" texto.txt 3000`
4. Crie o agendamento. O endereço é a base `https://zernio.com/api/v1` mais `/posts`, com a versão aparecendo uma vez só. `curl -sS -X POST 'https://zernio.com/api/v1/posts' -H "Authorization: Bearer $ZERNIO_API_KEY" -H 'Content-Type: application/json' --data @post.json -o recibo.json -w 'HTTP %{http_code}\n'`
5. Leia o código impresso. O sucesso da criação é **201**, não 200, e valor fora da faixa 200 a 299 vai para a seção de falhas.
6. Extraia o identificador. O recibo é aninhado: o objeto vem em `post` e o campo chama `_id`, com 24 caracteres hexadecimais. `python3 -c "import json;d=json.load(open('recibo.json'));p=d.get('post') or {};print(p.get('_id') or 'ERRO: '+str(d.get('error') or 'SEM IDENTIFICADOR'))"`
7. Se sair `ERRO: SEM IDENTIFICADOR`, o agendamento está NÃO VERIFICADO: procure o identificador no recibo inteiro antes de tentar de novo, porque um segundo POST com o mesmo texto vira duplicata. Qualquer outro motivo depois de `ERRO:` é a chamada que falhou, e o caminho é a seção de falhas.

## Subir a mídia do carrossel

A mídia entra no Zernio antes da criação, em três chamadas. Este caminho rodou em 2026-07-24 e levou o post do LinkedIn ao ar, mas não foi reexecutado: em 2026-07-29 só se confirmou que o endereço existe, porque sem credencial ele responde 401 em JSON, e caminho inexistente responde 200 com HTML. A assinatura devolve `uploadUrl` e `publicUrl`: o arquivo sobe por PUT na primeira, e a segunda vai para `mediaItems`, onde cada item leva `type` e `url`. O `_id` nasce no servidor e aparece só na leitura.

```bash
for slide in *-slide.png; do
  curl -sS -X POST 'https://zernio.com/api/v1/media/presign' -H "Authorization: Bearer $ZERNIO_API_KEY" -H 'Content-Type: application/json' --data "{\"filename\":\"$slide\",\"contentType\":\"image/png\"}" -o "presign-$slide.json" -w 'HTTP %{http_code}\n'
  curl -sS -X PUT --upload-file "$slide" -H 'Content-Type: image/png' "$(python3 -c "import json,sys;print(json.load(open(sys.argv[1]))['uploadUrl'])" "presign-$slide.json")" -w 'HTTP %{http_code}\n'
done
python3 -c "import json,glob;u=[json.load(open(f))['publicUrl'] for f in sorted(glob.glob('presign-*.json'))];print(len(u), json.dumps([{'type':'image','url':x} for x in u]))"
```

Um arquivo de assinatura por slide, nomeado pelo slide: gravar tudo em um arquivo só apaga o anterior e o carrossel chega incompleto. O PUT vai sem o header Authorization, porque a assinatura já viaja na própria URL, e a faixa de sucesso dele é 200 a 204. A contagem impressa na última linha prova que o número de slides bate, e a ordem da lista é a ordem do carrossel.

Antes de montar `mediaItems`, faça HEAD em cada `publicUrl` com `curl -sSI` e leia o content-type: tipo de imagem confirma bytes, `text/html` confirma que o endereço é página, e saída vazia não confirma nada. A URL pública do Zernio mora em `media.zernio.com/media/` mais carimbo de tempo, hash e nome do arquivo.

Duas restrições de tempo falham no dia da publicação, não na criação, e por isso se conferem antes do POST: mídia temporária não sobe mais de sete dias antes do horário, e o horário guarda 30 minutos de margem.

Antes de gastar upload, cheque o horário na listagem do Zernio, `GET /posts`, filtrando no seu código por plataforma e pelo `_id` da conta, e leia todas as páginas. Mesmo instante e mesmo conteúdo na mesma conta é idempotência: leia o post existente e confirme. Mesmo instante e conteúdo diferente é colisão: pare e devolva ao humano o identificador que já está lá. Outra conta ou outra plataforma no mesmo horário não é colisão. A autorização mostra plataforma, conta, instante com fuso, contagem de mídia e de caracteres, e só segue com um sim explícito.

## Confirmar o agendamento

O recibo do POST é aceite, não prova. A prova vem da segunda chamada ao Zernio, por caminho independente: o estado é `scheduled` e a contagem de `mediaItems` bate com a enviada.

```bash
curl -sS "https://zernio.com/api/v1/posts/O_ID_DO_PASSO_6" -H "Authorization: Bearer $ZERNIO_API_KEY" -o confirmacao.json -w 'HTTP %{http_code}\n'
python3 -c "
import json,datetime as dt
p=json.load(open('confirmacao.json')).get('post') or {}
c=(p.get('platforms') or [{}])[0].get('accountId')
print(p.get('_id'), p.get('status'), c.get('_id') if isinstance(c,dict) else c, len(p.get('mediaItems') or []))
a=dt.datetime.fromisoformat('2027-01-15T06:45:00-03:00')
print('mesmo instante:', a==dt.datetime.fromisoformat(p['scheduledFor'].replace('Z','+00:00')))"
```

A primeira linha sobrevive a post sem mídia e a conta devolvida como texto. Nunca compare o `scheduledFor` como texto: em 2026-07-28, `2027-01-15T06:45:00-03:00` voltou como `2027-01-15T09:45:00.000Z`, mesmo instante em outra string.

Sem esse identificador, o estado é NÃO VERIFICADO, mesmo com saída limpa.

## Quando parar

- Pare quando o GET devolver 200, o status for `scheduled`, o instante bater e a contagem de mídia for igual à enviada.
- Pare quando qualquer chamada devolver código fora da faixa 200 a 299, sem repetir o POST.
- Pare quando a mesma leitura tiver sido repetida duas vezes com o mesmo resultado, porque o objeto não muda sozinho.
- Não comece sem chave válida, conta ativa e autorização. Diante de HTTP 429 ou de 5xx, espere e releia, nunca repita o POST.

Apagar ou mover um agendamento do Zernio exige autorização do humano que nomeie o post ou o horário. A prova de remoção tem duas pontas: `DELETE /posts/{id}` devolve 200 com mensagem de remoção, e o GET seguinte passa a devolver 404. Foi assim que o post de teste desta edição saiu.

## Se o agendamento falhar

```bash
python3 -m json.tool recibo.json
```

O corpo carrega o motivo. O código HTTP não sobrevive para a chamada seguinte: se o perdeu, repita a leitura, nunca o POST.

| Sintoma na saída do Zernio | Ação |

|---|---|

| A checagem da chave devolve 0 | Chave ausente ou fora do formato. Pare e peça a chave ao humano. |

| `/accounts` vazio ou `isActive` em false | Conta não conectada. Peça ao humano conectar em zernio.com e pare. |

| HTTP 401 ou 403 | Confira o formato da chave e peça rotação ao humano. Não tente outra credencial. |

| HTTP 400 com `Invalid post ID format` | O identificador não tem os 24 caracteres hexadecimais. Confira antes da chamada. |

| Corpo começando em DOCTYPE, seja o código 200 ou 404 | O endereço não existe. Corrija o caminho antes de repetir. |

| HTTP 404 com corpo em JSON no GET logo após criar | O identificador foi lido do lugar errado. Ele mora em `post._id`. |

## O que não funciona no Zernio

**Se você já opera isso:** nenhuma das seis falhas abaixo devolve erro óbvio, e todas menos a última foram vistas na API do Zernio em 2026-07-28 e 2026-07-29.

- Endereço inexistente não se anuncia pelo código. Em 2026-07-28, `POST /validate/media` e `POST /media/validate` responderam **200 com o app em HTML**; em 2026-07-29, um GET em caminho inexistente respondeu 404 com a mesma página. O discriminante não é o código: é o primeiro caractere do corpo, menor que para página.
- Ler o identificador na raiz do recibo devolve vazio, porque o objeto vem aninhado em `post` e o campo chama `_id`. O discriminante é a chave `post` no topo da resposta.
- Comparar `scheduledFor` como texto reprova agendamento correto, porque o Zernio devolve normalizado em UTC. O discriminante é o sufixo Z na string devolvida.
- Na leitura, `platforms[0].accountId` volta como objeto expandido de sete campos: `_id`, `profileId`, `platform`, `displayName`, `isActive`, `profilePicture` e `username`. Comparar o enviado com o devolvido acusa diferença falsa, e o discriminante é o `_id` dentro desse objeto.
- A listagem de posts vem paginada e parece completa. Em 2026-07-28 o Zernio devolveu 10 posts de 12, e a checagem de colisão feita só na primeira página dá falso negativo. O discriminante é `pages` maior que 1 dentro de `pagination`.
- URL de visualização do Google Drive devolve HTML no lugar dos bytes, e o Zernio recebe endereço que não é mídia. O discriminante é o content-type, lido com `curl -sSI` antes de montar `mediaItems`.

## Como ler o resultado

| Padrão na resposta do Zernio | O que significa | O que fazer agora |

|---|---|---|

| 201 com `post._id` presente | Criado, ainda não confirmado | Rodar o GET antes de relatar |

| 200, status `scheduled`, instante igual | Agendamento de pé no horário certo | Relatar identificador, conta e horário |

| Instante diferente do enviado | O post sai em outra hora | Pedir autorização que nomeie o post, apagar e criar de novo |

| Mesmo instante e mesma conta, conteúdo diferente | Colisão de horário | Parar e devolver ao humano o post que já existe |

| Contagem de mídia menor que a enviada | Carrossel incompleto | Pedir autorização, apagar, subir o que faltou e criar de novo |

## A partitura desta edição

A tabela separa o que o humano decidiu do que a frota rodou, na execução de 2026-07-28 contra a API real do Zernio. Os carrosséis de 11 e de 14 mídias da listagem nasceram às 04:42Z, fora desta sessão.

| Humano decidiu | Agente executou |

|---|---|

| Decidiu não usar o clipe arquivado | Manteve a ordem de pé no Zernio |

| Mandou consultar a API antes de afirmar escopo | Leu a página 1 de 2 da listagem, 10 posts, e quatro contas ativas |

| Autorizou rodar contra a API real | Criou e confirmou um post de texto no LinkedIn, sem mídia |

| Autorizou apagar o teste | Rodou o DELETE e a leitura seguinte não achou mais o post |

## Quem operou o Zernio neste documento

Este texto é de José Carlos Amorim, publicado na Nexialismo em 2026-07-28. O procedimento foi executado contra a API real do Zernio, não reescrito da documentação: a publicação de 2026-07-24 no LinkedIn saiu pelo caminho REST, com o identificador urn:li:share:7486455115952906240, e o carrossel de 9 mídias no Instagram em 2026-07-27 saiu pelo mesmo caminho.

As falhas silenciosas de produção, cada uma com o discriminante dela, ficam em https://docs.nexialismo.ai. A versão em inglês deste documento fica em https://docs.nexialismo.ai/en/zernio-agent-first.

Contas públicas do autor, uma por formato: vídeo no YouTube em https://www.youtube.com/@josecarlosamorim-ai e imagem no Instagram em https://www.instagram.com/josecarlosamorim.ai/.

Verificado em 2026-07-28, com recheque de rotas em 2026-07-29. Próxima revisão em 2026-10-28. A versão 2.0 dizia que o Instagram nunca tinha ido ao ar pelo Zernio. A leitura da API mostrou seis posts de Instagram: quatro publicados em 2026-07-27 e 2026-07-28, um deles carrossel de 9 mídias, e dois agendados com 11 e 14 mídias. O erro veio de documentar o blueprint interno como estado atual, com a chave em mãos e sem consultar a API.

Tamo junto.
