Pular para o conteúdo principal

Envios de formulário

Cada vez que alguém preenche um formulário da instituição, o Workfuse guarda um envio: o que a pessoa digitou, qual contato foi reconhecido e qual versão dos termos ela aceitou. Esta página cobre a lista de envios, pensada para a sua aplicação consultar de tempos em tempos, e o detalhe de um envio, com o resultado por campo e o termo aceito.

Todos os caminhos são relativos a API_BASE/public/v1/crm, e toda requisição leva a API key com o módulo crm: Authorization: Bearer wf_sua_chave_aqui. A chave define a instituição: você só enxerga os envios dela.

Listar envios​

GET /form-submissions  ·  síncrono

curl "API_BASE/public/v1/crm/form-submissions?createdFrom=2026-10-01T00:00:00Z&pageSize=50" \
-H "Authorization: Bearer wf_sua_chave_aqui"

Os envios vêm em ordem de createdAt e, no empate, de id. A ordem é estável: paginar não repete nem pula envio.

Filtros​

ParâmetroDescrição
createdFromISO 8601. Inclusivo: traz envios com createdAt >= createdFrom.
createdToISO 8601. Exclusivo: traz envios com createdAt < createdTo.
formIdSó os envios deste formulário.
contactIdSó os envios deste contato (id do Workfuse).
idPropertyexternalId, email ou taxId. Com ele, contactId é lido como o valor desta propriedade. Exige contactId.
channelConsentEMAIL, WHATSAPP ou PUSH. Só os envios em que a pessoa marcou a caixa deste canal.
includeTesttrue inclui envios de teste. Padrão: false.
includeRelações a expandir, separadas por vírgula (include=form,contact) ou com o parâmetro repetido (include=form&include=contact). Veja abaixo.
pagePadrão 1.
pageSizePadrão e máximo 100. Acima disso, 400.

A janela de datas é meio-aberta ([createdFrom, createdTo)), então janelas consecutivas não contam o mesmo envio duas vezes. createdFrom depois de createdTo responde 400.

contactId é um filtro, não um recurso: um contato que não existe na instituição devolve uma página vazia (200), nunca 404.

O que vem na resposta​

Sem include, só o envio:

{
"data": [
{
"id": "clx1a2b3c4d5e6f7g8h9i0j1k",
"createdAt": "2026-10-02T10:00:00.000Z",
"isTest": false,
"form": { "id": "clf1a2b3c4d5e6f7g8h9i0j1k" },
"contact": { "id": "clz9q2k8r0001s6vp2h4m7x1e" },
"contactMatch": "EMAIL",
"values": { "email": "ana@exemplo.com", "name": "Ana Souza" },
"termsAcceptance": {
"termsVersion": 3,
"acceptedAt": "2026-10-02T10:00:00.000Z"
}
}
],
"pagination": { "page": 1, "pageSize": 50, "pages": 1, "items": 1 }
}
  • termsAcceptance vem sempre: é a versão dos termos que a pessoa aceitou ao enviar.
  • contactMatch diz como o contato foi reconhecido: EMAIL, CPF ou NEW_CONTACT. É null em envios gravados antes desta versão da API, que também vêm com fields vazio no detalhe.
  • contact é null quando o envio não tem contato (por exemplo, o contato foi excluído).
  • values é o que a pessoa digitou, por chave de propriedade. Um campo opcional deixado vazio vem como null, nunca como "".

Expandir com include​

ValorO que traz
formform completo: id, name, usageTags, publishedAt e institution ({ id, name }).
contactcontact com properties, igual ao GET /contacts/{id}.
contact.consentsComo contact, mais consents (um item por canal). Expande contact junto.
channelConsentsOs consentimentos que este envio gerou, um item por caixa marcada.

Um channelConsents vazio ([]) significa que a pessoa não marcou nenhuma caixa. O envio em si continua válido: os termos ficam em termsAcceptance.

curl "API_BASE/public/v1/crm/form-submissions?include=form,contact.consents,channelConsents" \
-H "Authorization: Bearer wf_sua_chave_aqui"
{
"id": "clx1a2b3c4d5e6f7g8h9i0j1k",
"form": {
"id": "clf1a2b3c4d5e6f7g8h9i0j1k",
"name": "Inscrição 2027",
"usageTags": ["site"],
"publishedAt": "2026-09-20T12:00:00.000Z",
"institution": { "id": "cli1a2b3c4d5e6f7g8h9i0j1k", "name": "Campus Centro" }
},
"contact": {
"id": "clz9q2k8r0001s6vp2h4m7x1e",
"properties": { "name": "Ana Souza", "email": "ana@exemplo.com" },
"consents": [
{ "channel": "WHATSAPP", "status": "GIVEN", "sendable": true },
{ "channel": "EMAIL", "status": "NONE", "sendable": false },
{ "channel": "PUSH", "status": "NONE", "sendable": false }
],
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-10-02T10:00:00.000Z"
},
"channelConsents": [
{
"id": "clc1a2b3c4d5e6f7g8h9i0j1k",
"channel": "WHATSAPP",
"eventType": "GIVEN",
"address": "+5511999999999",
"origin": { "id": "clo1a2b3c4d5e6f7g8h9i0j1k", "name": "Formulário" },
"consentedAt": "2026-10-02T10:00:00.000Z"
}
]
}

(O exemplo mostra um item de data, sem os campos que já apareceram acima.)

include desconhecido responde 400 com INVALID_INCLUDE e os valores aceitos em details.accepted. O evidence é do detalhe do envio e não é aceito na lista.

Expandir contact custa uma leitura por contato da página: com pageSize alto, prefira pedir só o que você usa.

Como sincronizar​

  1. Na primeira vez, peça sem createdFrom e pagine até page passar de pages.
  2. Guarde o maior createdAt que você recebeu.
  3. Na próxima consulta, use em createdFrom esse valor menos uma margem de 5 minutos. O createdAt é marcado quando o envio começa a ser gravado, não quando ele fica visível: um envio que ainda estava gravando na sua consulta anterior pode aparecer depois com um createdAt menor que o maior que você guardou. Sem a margem, ele seria pulado.
  4. Descarte os envios que você já tem, pelo id: com a margem (e com createdFrom inclusivo), os envios do fim da janela anterior voltam de propósito.

Ler um envio​

GET /form-submissions/{id}  ·  síncrono

curl "API_BASE/public/v1/crm/form-submissions/clx1a2b3c4d5e6f7g8h9i0j1k?include=form,channelConsents" \
-H "Authorization: Bearer wf_sua_chave_aqui"

É o mesmo item da lista, mais fields (o que aconteceu com cada campo) e o texto do termo aceito em termsAcceptance.termsText:

{
"id": "clx1a2b3c4d5e6f7g8h9i0j1k",
"createdAt": "2026-10-02T10:00:00.000Z",
"isTest": false,
"form": { "id": "clf1a2b3c4d5e6f7g8h9i0j1k", "name": "Inscrição 2027" },
"contact": { "id": "clz9q2k8r0001s6vp2h4m7x1e" },
"contactMatch": "EMAIL",
"values": { "email": "ana@exemplo.com", "city": null },
"fields": [
{
"propertyKey": "email",
"label": "E-mail",
"native": true,
"required": true,
"conflictStrategy": "KEEP_EXISTING",
"submittedValue": "ana@exemplo.com",
"result": "UNCHANGED"
},
{
"propertyKey": "city",
"label": "Cidade",
"native": false,
"required": false,
"conflictStrategy": "OVERWRITE",
"submittedValue": null,
"result": "NOT_APPLIED",
"reason": "NO_VALUE"
}
],
"termsAcceptance": {
"termsVersion": 3,
"termsText": "Aceito receber comunicações da instituição.",
"acceptedAt": "2026-10-02T10:00:00.000Z"
},
"channelConsents": []
}

O que aconteceu com cada campo​

fields vem na ordem do formulário. submittedValue é o que a pessoa digitou, ou null quando veio vazio. result diz o que o envio fez no contato:

resultSignificado
WRITTENO valor foi gravado no contato.
APPENDEDO valor foi somado ao que o contato já tinha.
UNCHANGEDO contato já tinha esse valor.
KEPT_EXISTINGO contato já tinha outro valor e a estratégia do campo o manteve.
NOT_APPLIEDNada foi gravado; reason explica: MATCH_KEY, PROPERTY_NOT_FOUND, INVALID_VALUE, NO_VALUE ou STRATEGY_NOT_ALLOWED.
PENDINGO processamento parou antes de confirmar a gravação: o valor pode ou não ter sido gravado. Confira o contato antes de repetir a escrita, sobretudo em COMPLEMENT.

reason só aparece em NOT_APPLIED.

fields e termsAcceptance são a foto do envio: editar o formulário depois, ou publicar uma nova versão dos termos, não muda o detalhe de um envio que já aconteceu.

Expandir com include​

Os mesmos valores da lista, mais evidence:

ValorO que traz
evidenceAcrescenta ipAddress e userAgent dentro de termsAcceptance. null quando o envio não guardou o dado (envio antigo, ou sem o header).

Sem evidence, as chaves ipAddress e userAgent não existem na resposta. IP e user agent são dado pessoal: peça só quando precisar da evidência do aceite.

Um envio de teste lê normalmente pelo id, com isTest: true.

Erros do detalhe​

HTTPQuando
400include desconhecido (INVALID_INCLUDE, com os aceitos em details.accepted).
401API key ausente, inválida ou revogada.
403A chave não tem o módulo crm no escopo (MODULE_NOT_IN_KEY_SCOPE), a conta de serviço não tem integration.public-crm.use no papel (PERMISSION_DENIED), ou a organização não tem a capability crm (CAPABILITY_NOT_ENABLED).
404O envio não existe ou é de outra instituição (FORM_SUBMISSION_NOT_FOUND). A resposta é a mesma nos dois casos.

Erros da lista​

HTTPQuando
400Data inválida ou invertida, pageSize acima de 100, idProperty sem contactId, ou include desconhecido.
401API key ausente, inválida ou revogada.
403A chave não tem o módulo crm no escopo (MODULE_NOT_IN_KEY_SCOPE), a conta de serviço não tem integration.public-crm.use no papel (PERMISSION_DENIED), ou a organização não tem a capability crm (CAPABILITY_NOT_ENABLED).

Não existe 404 na lista: um envio de outra instituição simplesmente não aparece.