Validate Barcode Scans with a Webhook

Validate barcode scan data on your server before it saves. When a user scans a barcode or edits a field in the Orca Scan mobile app, an HTTP POST request sends the captured data to your webhook endpoint. Your server can then verify, reject, or modify the data and return in-app error messages or notifications to guide the user - before anything is saved.

How does the Validation URL work?

  1. A user scans a barcode or edits a field in the Orca Scan mobile app
  2. The full data is sent to your Validation URL via HTTP POST
  3. Your server inspects the incoming data
  4. You return a response to either:
    • allow the data to be saved
    • modify the data before saving
    • block the save with an error
  5. Orca Scan applies the response and updates the user interface

Note: Your endpoint must respond within 750ms or the result is ignored.

How do I add a Validation URL to my sheet?

  1. Log in to the Orca Scan web app
  2. Select the sheet you want to validate
  3. Click Integrations in the toolbar Orca scan integrations button
  4. Click Events API and enter your Validation URL Orca scan set validation url
  5. Click Test to confirm your Validation URL is accessible
  6. Save the changes

That’s it. Now scan a barcode → enter some data → tap save to validate.

How do I create a Validation URL?

You can create a Validation URL using a few lines of code:

Node.js C# Go Python PHP Java
const app = express(); app.use(express.json()); app.post('/', function(request, response){ data = request.body; // dubug purpose: show in console raw data received console.log("Request received: \n"+JSON.stringify(data, null, 2)); // orca system fields start with ___ // access the value of fields using field name // example: data.Name, data.Barcode, data.Location const name = data.Name // validation example if (name.length > 20) { // return error message to show user response.json({ "title": "Invalid Name", "message": "Name cannot contain more than 20 characters", }).send(); return; } // return HTTP Status 204 (no content) response.status(204).send(); }); app.listen(3000, () => console.log('Example app is listening on port 3000.'));
[ApiController] [Route("/")] public class OrcaValidationDotNet : ControllerBase { [HttpPost] [Consumes("application/json")] public async Task<ActionResult> validationReceiver() { using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8)) { // get the raw JSON string string json = await reader.ReadToEndAsync(); // convert into .net object dynamic data = JObject.Parse(json); // orca system fields start with ___ // access the value of fields using field name // example: data.Name, data.Barcode, data.Location string name = (data.Name != null) ? (string) data.Name : ""; // validation example if (name.Length > 20) { // return error message to show user return Ok(new { title = "Name is too long", message = "Name cannot contain more than 20 characters" }); } } // return HTTP Status 204 (no content) return NoContent(); } }
func validationHandler(w http.ResponseWriter, r *http.Request) { // Read body body, err := ioutil.ReadAll(r.Body) defer r.Body.Close() if err != nil { fmt.Println(err) http.Error(w, err.Error(), 500) return } // Parse JSON data var barcode OrcaBarcode jsonErr := json.Unmarshal([]byte(body), &barcode) if jsonErr != nil { fmt.Println(jsonErr) http.Error(w, jsonErr.Error(), 500) return } // dubug purpose: show in console raw data received fmt.Println(barcode) // orca system fields start with ___ // access the value of fields using field name // example: data.Name, data.Barcode, data.Location name := barcode.Name // validation example if (len(name) > 20) { // return error message to show user w.Write([]byte(`{ "title": "Invalid Name", "message": "Name must be less than 20 characters"} `)) return } // return HTTP Status 204 (no content) w.WriteHeader(204) }
@app.route('/', methods=['POST']) def orca_validation(): if request.method == 'POST': data = request.get_json() # dubug purpose: show in console raw data received print("Request received: \n"+json.dumps(data, sort_keys=True, indent=4)) # orca system fields start with ___ # access the value of fields using field name # example: data["Barcode"], data["Location"] name = data["Name"] # validation example if(len(name) > 20): # return error message to show user return json.dumps({ "title": "Invalid Name", "message": "Name cannot contain more than 20 characters", }) # return HTTP Status 204 (no content) return '', 204
if (preg_match('/$/', $_SERVER["REQUEST_URI"])) { if ($_SERVER['REQUEST_METHOD'] === 'POST') { $data = json_decode(file_get_contents('php://input'), true); // orca system fields start with ___ // access the value of fields using field name // example: data.Name, data.Barcode, data.Location $name = $data["Name"]; // validation example if (strlen($name) < 20) { // return error message to show user echo json_encode(array( "title" => "Invalid Name", "message" => "Name cannot contain more than 20 characters" )); exit; } // return HTTP Status 204 (No Content) http_response_code(204); exit; } }
@RequestMapping( value = "/", method = RequestMethod.POST) String index(@RequestBody Map<String, Object> data) throws Exception { // dubug purpose: show in console raw data received System.out.println(data); // orca system fields start with ___ // access the value of fields using field name // example: data.get("Barcode"), data.get("Location") String name = data.get("Name").toString(); // validation example if (name.length() > 20) { // return error message to show user JSONObject json = new JSONObject(); json.put("title", "Invalid Name"); json.put("message", "Name cannot contain more than 20 characters"); return json.toJSONString(); } // return HTTP Status 204 (no content) return ""; }

Full working examples for each language are available on GitHub.

HTTP Headers

Each Validation request includes HTTP headers that identify the source sheet, the user who triggered the barcode scan, and the request type. Use these headers to verify the request, apply sheet specific logic, and log or audit barcode scan activity.

Parameter Description
orca-sheet-id Unique ID of the Orca Scan sheet
orca-sheet-name The name of the sheet making the request
orca-user-email Email of user scanning the barcode (requires HTTPS)
orca-timestamp UNIX epoch time of the request
orca-secret Sheet assigned secret (check this to verify request)
orca-request-type validation

Barcode Scan Validation FAQs

How do I reject invalid data?

Return a HTTP status code 400 to reject data.

How do I show an error dialog in the app?

Return a HTTP status code 400 along with an ___orca_message with display set to dialog and type set to error, for example:

{
	"___orca_message": {
		"display": "dialog",
		"type": "error",
		"title": "Validation Error",
		"message": "Please provide more information"
	}
}

A dialog will appear and the user will be unable to proceed until resolved:

Validation URL in app dialog
Validation URL in app dialog

How do I show a notification in the app?

Return an ___orca_message with display set to notification, for example:

{
    "___orca_message": {
		    "display": "notification",
		    "type": "success",
		    "message": "Please scan next item"
    }
}

A notification will appear briefly at the top of the app:

Validation URL in app notification
Validation URL in app notification

There are three types of notifications to choose from:

Type Appearance
error A red notification appears at the top of the app.
warning A yellow notification appears at the top of the app.
success A green notification appears at the top of the app.

Can the Validation URL modify the data?

Yes. Simply return a HTTP status code 200 along with the modified data as a JSON object. The Orca barcode app will apply these changes, and, if no ___orca_message is present, allow the user to save:

{
    "barcode": "hello@orcascan.com",
    "notes": "Not that important :)"
}

How do I connect my development machine?

Expose your local service to the internet so Orca Scan can send Validation requests to it. The easiest way is using localtunnel:

# expose port 3000 to the internet (change if needed)
npx localtunnel --port 3000

Use the temporary HTTPS URL as your Validation URL during development, then replace it with a permanent public URL when deploying.

How do I secure my Validation URL?

Add a secret to your sheet and validate it on every request. The secret is sent as an HTTP header, or can be passed as a query parameter if required.

Need help setting up a Validation URL?

We’re happy to help, chat with us live or book a free implementation call.

Ready to start scanning?


Close Icon
Get the Orca scan app QR code

Scan the QR code to download the mobile app

Scan the QR code to open this guides on your mobile device