Handling failed submissions and offline users

Keep the visitor's text on screen whenever a send fails. Retry network errors and 429 responses with a short backoff, and fix the request for 400, 403, 404 and 413, since retrying those will fail again. Test each path with the network turned off and with a bad key.

By 路 Published 路 Last updated

What do the feedback API error codes mean?

The REST API returns a status code for every failed request, and each one points to a different fix. A 400 means the body is invalid, most often because the key or the message is missing or shorter than two characters. A 403 means the origin of the request is not on the product's allowed list. A 404 means the key does not match any product. A 413 means the body is larger than 16 KB. A 429 means the same product received more than 10 requests per minute from the same IP address.

A successful submission returns 201 with a JSON body that holds ok: true and the new feedback id. Treat anything else as a failure and show the visitor a message that matches the cause.

Which failures should you retry and which should you fix?

Retrying a request that will fail again only wastes the visitor's time and adds load to the server. Sort the codes into two groups:

Code Meaning Action Retry?
400 The body is invalid or a required field is missing Check the key, the message length and the field types No, fix the request
403 The site origin is not allowed Add the site to the product's allowed origins No, fix the settings
404 The public key is unknown Check the key, and update it after a key rotation No, fix the key
413 The body is over 16 KB Shorten the message or drop large metadata No, shorten it
429 Too many requests from one IP for this product Wait, then send again with a delay Yes, with backoff
Network error The request never reached the server Keep the text and offer to try again Yes, when the connection returns

The rate limit is per IP and per product, so a shared office network can hit it faster than a single person would. A short delay usually clears it.

How do you keep the message when a send fails?

The worst outcome is a failed send that clears the text. Keep the message in state until the server confirms success, and only clear it after a 201 response. If the send fails, leave the text in place, show a clear message next to the send button and move focus to that message so a screen reader user hears it.

async function submitFeedback(payload) {
  const response = await fetch("https://escutaproduto.com/api/v1/feedback", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
  if (response.status === 201) return { ok: true };
  return { ok: false, status: response.status };
}

The function returns the status instead of throwing, so the form can choose the message for each code. A network failure makes fetch reject, so wrap the call in try and catch and treat a rejection as a network error.

How do you back off after a 429 response?

Retry a 429 a few times with growing delays, then stop and tell the visitor to try again later. Limit the retries, so a broken form does not loop forever:

async function sendWithBackoff(payload, attempt = 0) {
  let result;
  try {
    result = await submitFeedback(payload);
  } catch (error) {
    result = { ok: false, status: 0 };
  }
  if (result.ok) return result;
  const retryable = result.status === 0 || result.status === 429;
  if (!retryable || attempt >= 3) return result;
  await new Promise((resolve) => setTimeout(resolve, 1000 * 2 ** attempt));
  return sendWithBackoff(payload, attempt + 1);
}

The delays are one, two and four seconds. Show the visitor a status line while the retries run, so the form does not look frozen.

How do you handle a visitor who is offline?

Offline visitors are a different case. Do not guess the connection state from navigator.onLine alone, because the browser reports a connection even when the server cannot be reached. Instead, rely on the failed request itself, and use the online event to retry a message that is waiting.

For your own form, you can store an unsent message in localStorage and send it when the connection returns. Wrap every read and write in try and catch, because storage can be full or blocked in private windows. Remove the stored draft only after the server returns 201. Keep the saved text private to the visitor's browser, and never store anything you would not want on a shared computer.

What does the widget do on failure, and how do you test it?

Do not assume the widget behaves like your own form. Test it. Open the widget, type a message, turn off the network in DevTools or switch off Wi-Fi, then send. Check whether the text is still in the panel when the network returns, and whether the visitor can send it again without retyping. Then repeat the test with a page that is not in the allowed origins list, which produces a 403. Write down what you see, and adjust the instructions on your site. If the text is lost on a network failure, tell visitors to copy a long message before they send.

Run these checks before you release a form change:

  1. Send a valid message and confirm the 201 path clears the form.
  2. Turn off the network, send, and confirm the text stays.
  3. Send from an origin that is not allowed and confirm the 403 message makes sense to a visitor.
  4. Send a message of 17 KB and confirm the 413 message appears.
  5. Send several messages in a row and confirm the 429 path shows a wait message.

Recovering failed sends in an Escuta Produto integration

Use the widget for the common path and your own form only when you need custom error text or a different layout. The request format, the allowed origins and the key rules are in the API docs. The steps for adding the widget to a page are in the widget docs. For the other side of a failed send, when a message does arrive, how to triage customer feedback explains how to sort it. For spam protection that can cause a request to be rejected, how to protect a feedback form from spam covers the origin and rate rules in more depth.

Frequently asked questions

Should a feedback form retry every failed submission?

No. Retry network errors and 429 responses, with a short delay and a limit on attempts. Fix the request for 400, 403, 404 and 413, because sending the same body again will fail the same way.

What should happen to the visitor's message when a send fails?

The text should stay in the form. Clear it only after the server confirms success with a 201 response, and show a message that says what went wrong and what to do next.

What does a 429 error mean for a feedback form?

The same IP address sent too many requests to the same product in a short period, which is more than 10 per minute. Wait a few seconds, then send again, and do not loop retries without a limit.