Airtable
Validate Airtable attachments with a "Run a script" automation that sends the attachment URL to Constaia and writes the verdict to the record.
In Airtable, an automation can analyze a record's attachment as soon as it is uploaded: the Run a script action
sends the attachment URL to Constaia as file_url and writes the verdict and extracted data to fields on the same
record.
Prerequisites
- An Airtable base where you can create automations.
- A test key
ck_test_…from the dashboard. See Authentication. - A table, for example
Applications, with these fields:
| Field | Type |
|---|---|
Document | Attachment |
Verdict | Single line text |
Reasons | Long text |
Document number | Single line text |
Analysis ID | Single line text |
Create the automation
Choose the trigger
Create an automation with When a record matches conditions (condition: Document is not empty and
Verdict is empty) or When a record is updated on the Document field.
Add the Run a script action
Add Run a script and, under Input variables, create recordId with the trigger's Airtable record ID.
Paste the script
const API_KEY = "ck_test_..."; // see "Security" below
const table = base.getTable("Applications");
const { recordId } = input.config();
const record = await table.selectRecordAsync(recordId);
const attachments = record?.getCellValue("Document") ?? [];
if (attachments.length === 0) {
output.set("status", "no_document");
} else {
const attachment = attachments[0];
const res = await fetch("https://api.constaia.com/v1/analyze", {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
file_url: attachment.url,
filename: attachment.filename,
options: {
expect: ["es_dni", "es_nie", "passport"],
checks: { min_age_years: 18 },
language: "en",
metadata: { airtable_record_id: recordId },
},
}),
});
const analysis = await res.json();
if (!res.ok) {
const e = analysis.error;
await table.updateRecordAsync(recordId, { Verdict: "error", Reasons: `${e.code}: ${e.message} (${e.request_id})` });
throw new Error(e.message);
}
const verdict = analysis.verdict;
const number = analysis.fields.document_number ?? analysis.fields.nie_number;
await table.updateRecordAsync(recordId, {
Verdict: verdict ? verdict.status : analysis.status,
Reasons: (verdict?.reasons ?? [])
.filter((r) => r.severity !== "info")
.map((r) => r.message)
.join("\n"),
"Document number": number?.value ?? "",
"Analysis ID": analysis.id,
});
output.set("status", verdict?.status ?? analysis.status);
}Airtable attachment URLs are https and downloadable for a limited time, which is enough for Constaia to download
them right away (20 MB and 15 s maximum). If you prefer a Single select field for Verdict, write
{ name: verdict.status } instead of the text.
Act on the verdict
With the script's status output, add conditional actions (Add conditional group):
status | What to do |
|---|---|
Válido valid | Mark the application as approved |
No válido invalid | Email the applicant with the reasons |
Revisar review | Assign the record to a person. See Human review |
If the analysis takes longer than 30 s, the API responds 202 and the script writes queued or processing to
Verdict (see the next section). What each status means is in Verdicts.
Long documents: async and webhook
Automation scripts have a maximum run time. For PDFs with many pages, send "async": true in the options: the API
responds 202 immediately and notifies you by webhook when it finishes.
- Create another automation with the When webhook received trigger and register its URL in the dashboard or
with
POST /v1/webhook-endpoints(see Webhooks). - Airtable can't verify the event's HMAC signature, so don't trust its content: in a Run a script, take
data.idfrom the body, check it starts withan_, read the analysis again withGET https://api.constaia.com/v1/analyses/{id}and your key, and update the record given in that response'smetadata.airtable_record_id. - Constaia retries failed deliveries: if the record already has a verdict, do nothing.
Don't use keep_results: false in this flow, or the re-read returns 404.
Test in test mode
With a ck_test_… key no credits are used and the response depends on the file name (filename). Upload any
real image as an attachment with one of these names:
| Attachment | Result |
|---|---|
dni_valid.jpg | Válido Document number = 12345678Z |
nie.jpg | Válido Document number = X1234567L |
dni_expired.jpg | No válido reason not_expired with severity error |
blurry.jpg | Revisar reason low_quality |
More scenarios in Test mode.
Security
- The key is written in the script and anyone with creator permissions on the base can see it. Use
ck_live_…keys only in bases with restricted access, not in bases shared with outsiders or in templates. If you can't restrict access, make the script call a backend of yours that holds the key. - Build and test the automation with a test key.
- Airtable keeps the data you write to the record and the original attachment. Write only the fields you need.
Constaia deletes its copy of the file when it finishes with
storage: "none"(the default); see Storage and privacy.
Limits
- 20 MB per file; PDFs up to 30 pages synchronously and up to 200 with
async: true. - 2 requests per second per key on the free plan (10 on paid). If you import many records at once and many automations fire, you may get 429
with
Retry-After. See Rate limits and, for bulk loads, batches.
Next steps
Google Apps Script
Validate Google Drive documents from Google Sheets with Apps Script and UrlFetchApp, keep the key in Script Properties and write the verdict to the sheet.
Retool
Build an internal Retool panel to validate documents with Constaia: a REST resource with the key in configuration variables, file upload and results.