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:
- o humano aprovou o texto;
- o horário está em ISO 8601 com offset;
- a conta de destino tem o
_iddo Zernio; - a autorização do humano é explícita.
Só existe sucesso com as quatro provas:
- o POST devolve 201 e cria o recurso;
- o GET pelo identificador devolve 200;
- o status é
scheduled; - 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
- Os três níveis de leitura
- O agendamento fantasma
- Quem decide o quê
- Antes de começar
- Os passos do agendamento
- Subir a mídia do carrossel
- Confirmar o agendamento
- Quando parar
- Se o agendamento falhar
- O que não funciona no Zernio
- Como ler o resultado
- A partitura desta edição
- 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ê
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
/accountscomisActivetrue. - 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
- Descubra o identificador da conta. O campo chama
_id, e a conta ativa trazplatform,displayNameeisActive. 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']])" - Monte o corpo da requisição. O
accountIdfica dentro deplatforms, nunca na raiz, porque cada destino carrega a conta dele. Na criação ele vai como texto simples, e o carrossel entra emmediaItems, 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" } - 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 - Crie o agendamento. O endereço é a base
https://zernio.com/api/v1mais/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' - 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.
- Extraia o identificador. O recibo é aninhado: o objeto vem em
poste 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'))" - 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 deERRO:é 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.
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.
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
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/mediaePOST /media/validateresponderam 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
poste o campo chama_id. O discriminante é a chavepostno topo da resposta. - Comparar
scheduledForcomo texto reprova agendamento correto, porque o Zernio devolve normalizado em UTC. O discriminante é o sufixo Z na string devolvida. - Na leitura,
platforms[0].accountIdvolta como objeto expandido de sete campos:_id,profileId,platform,displayName,isActive,profilePictureeusername. Comparar o enviado com o devolvido acusa diferença falsa, e o discriminante é o_iddentro 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 é
pagesmaior que 1 dentro depagination. - 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 -sSIantes de montarmediaItems.
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.