JavaScript e fetch
O endpoint de envio em detalhe: formatos de corpo, headers, CORS, respostas e idempotência.
Qualquer código que consiga fazer uma requisição HTTP pode enviar para o Ikiform. Envie JSON, FormData ou dados urlencoded para https://ikiform.com/f/<formId> e leia uma resposta JSON. O SDK encapsula isso em uma função, mas é opcional. Para uma configuração passo a passo em uma stack específica, veja Guias por plataforma.
Com fetch
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({
name: "Ada Lovelace",
email: "ada@example.com",
message: "Hello",
}),
});
const result = await response.json();
if (!result.ok) console.error(result.error.code, result.error.fields);Experimente
curl https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx \
-H "Content-Type: application/json" \
-d '{"name":"Ada Lovelace","email":"ada@example.com","message":"Hello from the Ikiform docs."}'Digite o ID do seu formulário para enviar isto como uma requisição real.
Endpoints
| Método | Caminho | O que faz |
|---|---|---|
POST | /f/<formId> | Cria um envio |
OPTIONS | /f/<formId> | Preflight de CORS, sempre 204 |
GET | /f/<formId>/schema | A definição pública de campos do formulário, veja shadcn/ui |
GET | /f/<formId>/thanks | A página de agradecimento hospedada |
GET | /f/<formId>/challenge | O frame de verificação humana do Ikiform, usado pelo SDK |
Abrir o próprio /f/<formId> em uma aba do navegador é um GET, que responde 404 not_found. Isso é esperado: o endpoint só aceita envios.
Formatos de corpo
| Content-Type | Formato | Observações |
|---|---|---|
application/json e tipos +json | Um objeto JSON | Os valores podem ser strings, números, booleanos, arrays e objetos, com no máximo 3 níveis de aninhamento contando o objeto de topo |
application/x-www-form-urlencoded | Pares de nome e valor | Nomes repetidos e name[] viram arrays |
multipart/form-data | Pares de nome e valor | Igual a urlencoded; arquivos de verdade são recusados |
| Qualquer outro | 415 unsupported_media_type |
O corpo precisa ser UTF-8. Um charset diferente de utf-8 recebe 415 unsupported_charset, e bytes que não são UTF-8 válido recebem o mesmo código. Um array JSON ou um corpo malformado recebe 400 invalid_body.
Headers
Headers da requisição
| Header | Obrigatório | Efeito |
|---|---|---|
Content-Type | Sim | Escolhe o parser do corpo, veja acima |
Accept | Não | application/json força uma resposta JSON, veja JSON ou redirecionamento |
Idempotency-Key | Não | Torna as novas tentativas seguras, até 255 caracteres, veja Idempotência |
Authorization | Não | Bearer ik_sk_... ou Bearer ik_pub_..., veja A partir de um servidor |
Origin, Referer | Enviados pelos navegadores | Comparados aos domínios permitidos do formulário |
Headers da resposta
| Header | Quando | Valor |
|---|---|---|
X-Request-Id | Toda resposta | req_..., repetido como request_id nos corpos de erro |
Retry-After | 429 | Segundos até o próximo envio ser permitido |
Location | 303 sucesso no navegador | Seu redirecionamento ou a página de agradecimento hospedada |
Access-Control-Allow-Origin | Quando a requisição tem um Origin | A própria origem da requisição |
Access-Control-Expose-Headers | Com CORS | X-Request-Id, Retry-After, para o código no navegador conseguir ler os dois |
Cache-Control | Sucesso | no-store |
CORS
O endpoint de envio aceita envios cross-origin de qualquer site. Preflights sempre dão certo e devolvem a origem da requisição. Nenhum cookie ou credencial é usado, então não é o CORS que protege seu formulário: são os domínios permitidos. Com domínios permitidos definidos, um envio de outro site é armazenado como spam ou recusado, conforme a configuração do formulário. Veja Domínios permitidos.
OPTIONS /f/frm_xxxxxxxxxxxxxxxx HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Content-Type, Accept, Idempotency-Key, Authorization
Access-Control-Max-Age: 86400
Vary: Origin/api/v1 é diferente: ela não envia headers de CORS e responde preflights do navegador com 405, então uma chave secreta nunca pode ser usada a partir de uma página web.Respostas
Sucesso é 201 com o id do novo envio. Os erros usam o mesmo envelope em todo lugar, com erros de campo indexados pelo id do campo quando a validação falha. Todos os códigos estão em Erros.
{ "ok": true, "id": "sub_01k7v2c9t3e8m5q4x6z0a1b2c3" }201. Spam é armazenado à parte e nunca chega aos seus webhooks nem à sua caixa de entrada.Idempotência
Envie um header Idempotency-Key, ou um campo _idempotency, para tornar as novas tentativas seguras. As chaves têm até 255 caracteres e valem por formulário durante 24 horas. O header vence quando os dois são enviados.
| Nova tentativa com | Resultado |
|---|---|
| A mesma chave e os mesmos campos | A primeira resposta é repetida. Nenhum segundo envio é criado. |
| A mesma chave e campos diferentes | 422 idempotency_mismatch |
| A mesma chave enquanto a primeira requisição ainda está rodando | 409 idempotency_in_progress |
Requisições que falham antes de o envio ser armazenado, como erros de validação, não guardam a chave, então você pode corrigir os dados e tentar de novo com a mesma chave. Um loop seguro de novas tentativas:
const key = crypto.randomUUID();
async function send(attempt = 1) {
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": key },
body: JSON.stringify({ email: "ada@example.com" }),
}).catch(() => null);
if (response && response.status < 500 && response.status !== 429) return response.json();
if (attempt === 3) throw new Error("Ikiform is unreachable");
const wait = Number(response?.headers.get("Retry-After")) || attempt * 2;
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return send(attempt + 1);
}Guias por plataforma
Frameworks
- Next.jsUm client component ou uma server action.
- ReactEstados de carregamento, erro e sucesso.
- VueUm single-file component.
- NuxtEnvios antes da hidratação e com
$fetchdepois. - SvelteUm componente Svelte 5 e uma server action.
- SvelteKitUma página pré-renderizada com progressive enhancement.
- AstroUm formulário sem JavaScript, com melhoria opcional.
- GatsbyUm formulário estático ou uma Gatsby Function.
- JavaScript puro
fetchcom erros inline.
Geradores de sites estáticos
- HugoUm partial e um shortcode para arquivos de conteúdo.
- JekyllUm include e a configuração do GitHub Pages.
- EleventyUm shortcode para Nunjucks, Liquid e Markdown.