Ocriva Logo

Documents

Google Sheets Integration

Automatically log extracted document data to Google Sheets.

integrationsgoogle-sheetsspreadsheetautomation

Published: 4/5/2026

Google Sheets Integration

Automatically append extracted document data as rows in a Google Sheet every time Ocriva processes a document. This integration is ideal for teams that need a running log of processed files, extracted values, and confidence scores without writing any backend infrastructure — Google Apps Script handles everything.


Prerequisites

  • A Google account with access to Google Drive and Google Sheets
  • A Google Sheet where data will be written
  • An Ocriva organization with available webhook quota

How It Works

Ocriva sends a flattened JSON payload to a Google Apps Script web app you deploy from within your spreadsheet. The Apps Script function receives the POST request, parses the payload, and calls appendRow() to write the data as a new row. Because the web app URL is generated by Google and contains a long random string, it acts as an unguessable endpoint — no additional authentication layer is required.

Ocriva event fires
      |

POST JSON payload → Apps Script web app URL
      |

doPost() parses payload
      |

appendRow() writes data to Google Sheet

Step 1: Create a Google Sheet

  1. Go to Google Sheets and create a new spreadsheet
  2. Name it something descriptive, for example Ocriva Document Log
  3. In row 1, add column headers that match the data you want to capture:
| Timestamp | Event Type | File Name | Status | Confidence | Extracted Data |

The order of these headers must match the order in which your Apps Script writes data (see Step 2). You can add, remove, or reorder columns — just keep the script in sync.

TIP

Freeze row 1 (View → Freeze → 1 row) so headers stay visible as data grows. You can also apply bold formatting to the header row to make the sheet easier to read at a glance.


Step 2: Create an Apps Script Web App

  1. In your Google Sheet, go to Extensions → Apps Script
  2. A new Apps Script project opens in a browser tab, pre-populated with an empty myFunction
  3. Delete all existing code and replace it with the following:
function doPost(e) {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var data = JSON.parse(e.postData.contents);
 
  sheet.appendRow([
    new Date().toISOString(),          // Timestamp — when the row was written
    data.eventType,                    // e.g. "document.processed"
    data.fileName,                     // Original file name
    data.status,                       // "success" or "failed"
    data.confidence,                   // Confidence score (0–1)
    JSON.stringify(data.extractedData) // Full extracted data as a JSON string
  ]);
 
  return ContentService
    .createTextOutput(JSON.stringify({ success: true }))
    .setMimeType(ContentService.MimeType.JSON);
}

What Each Line Does

LinePurpose
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()Opens the sheet this script is bound to and selects the first (active) tab
JSON.parse(e.postData.contents)Parses the raw POST body that Ocriva sends into a JavaScript object
sheet.appendRow([...])Adds a new row at the bottom of all existing data — never overwrites
new Date().toISOString()Records when the row was written (server time), not when the event fired
JSON.stringify(data.extractedData)Serializes the nested extracted data object to a readable string for storage in a single cell
ContentService.createTextOutput(...)Returns a 200 OK JSON response so Ocriva marks the delivery as successful
  1. Click Save (the floppy disk icon or Ctrl+S) and give the project a name, for example Ocriva Webhook Receiver

Step 3: Deploy as Web App

  1. In the Apps Script editor, click Deploy → New deployment
  2. Click the gear icon next to Type and select Web app
  3. Configure the deployment settings:
    • Description: Ocriva webhook receiver (optional, for your records)
    • Execute as: Me — the script runs with your Google account's permissions to write to the sheet
    • Who has access: Anyone — this is required so Ocriva's servers can reach the endpoint without authentication
  4. Click Deploy
  5. If prompted, click Authorize access and follow the OAuth consent screen to grant the script permission to edit your spreadsheet
  6. After authorization, Google displays the Web app URL in the format:
https://script.google.com/macros/s/AKfycb.../exec
  1. Click Copy next to the URL — you will paste this into Ocriva in the next step

WARNING

Setting "Who has access" to Anyone is required for Ocriva to deliver webhooks. The URL contains a long random string (AKfycb...) that functions as a shared secret — only someone with the exact URL can POST to it. Do not share this URL publicly or commit it to source control.


Step 4: Configure in Ocriva

  1. In the Ocriva dashboard, navigate to Integrations in the left sidebar
  2. Find the Google Sheets template card and click it
  3. Paste the Apps Script web app URL into the URL field
  4. Under Events, select the events you want to log (default: document.processed)
  5. Review the pre-filled payload template — it is already flattened for spreadsheet use (see Payload Template)
  6. Click Create Endpoint

Ocriva begins delivering events to your spreadsheet immediately. Upload a test document to verify that a new row appears.


Payload Template

The default Google Sheets payload template flattens the Ocriva event into top-level keys so the Apps Script can access them directly without nested lookups:

{
  "eventType": "{{eventType}}",
  "fileName": "{{payload.fileName}}",
  "status": "{{payload.status}}",
  "extractedData": "{{payload.extractedData}}",
  "processedAt": "{{payload.processedAt}}",
  "confidence": "{{payload.confidence}}"
}

Variable Reference

VariableDescriptionExample
{{eventType}}The event that fireddocument.processed
{{payload.fileName}}Original file nameinvoice-2026-03.pdf
{{payload.status}}Processing resultsuccess or failed
{{payload.extractedData}}Nested object of AI-extracted fields{"total": "1200", "vendor": "Acme"}
{{payload.processedAt}}ISO 8601 timestamp from the API2026-04-05T09:30:00Z
{{payload.confidence}}Model confidence score0.94

This flat structure means the Apps Script reads data.fileName rather than data.payload.fileName, which keeps the script code simple.

NOTE

Webhook payload template variables are delivered as strings. This means data.confidence arrives as a string like "0.94" rather than the number 0.94. If you use Google Sheets formulas such as =AVERAGE() on the Confidence column, the values must be numeric. Call parseFloat(data.confidence) in your Apps Script before writing the value to the sheet (see the Advanced section below).


Advanced: Parsing Extracted Data into Separate Columns

The default script stores extractedData as a single JSON string. If you want each extracted field in its own column — useful for filtering, sorting, and building charts — replace the doPost function with this version:

function doPost(e) {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var data = JSON.parse(e.postData.contents);
  var extracted = data.extractedData || {};
 
  sheet.appendRow([
    new Date().toISOString(),
    data.fileName,
    extracted.invoice_number || '',
    extracted.vendor        || '',
    extracted.total_amount  || '',
    extracted.currency      || '',
    parseFloat(data.confidence)
  ]);
 
  return ContentService
    .createTextOutput(JSON.stringify({ success: true }))
    .setMimeType(ContentService.MimeType.JSON);
}

Update your sheet headers to match:

| Timestamp | File Name | Invoice Number | Vendor | Total Amount | Currency | Confidence |

Replace the field names (invoice_number, vendor, etc.) with the keys your Ocriva template actually extracts. To find these keys, open a processed document in Ocriva, go to the Result tab, and note the exact key names in the extracted data object.

NOTE

After changing the script, you must create a new deployment (Deploy → New deployment). Editing the existing deployment's code without redeploying does not take effect. Copy the new URL and update the endpoint in Ocriva.


Troubleshooting

Script error — no row is added

  1. In the Apps Script editor, open the Executions tab (left sidebar, clock icon)
  2. Find the most recent execution and click it to see the full stack trace
  3. Common causes: a missing field in data that the script tries to read, or a JSON parse error if the payload is malformed

HTTP 403 Forbidden in Ocriva delivery logs

The web app was deployed with "Who has access" set to something other than Anyone. Redeploy: Deploy → New deployment → Who has access: Anyone. Update the URL in Ocriva with the new deployment URL.

Data not appearing in the right sheet

getActiveSheet() returns whichever tab was active when the script last ran. If you have multiple tabs, call the target sheet by name to be explicit:

var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Document Log");

Replace "Document Log" with the exact name of your tab (case-sensitive).

Updating the script after initial deployment

ActionResult
Edit code, click Save, no redeployOld code still runs — changes have no effect
Edit code, Deploy → Manage deployments → Edit (pencil icon) → change Version to "New version" → DeploySame URL, updated to latest code — use this for all edits
Deploy → New deploymentNew URL — update Ocriva endpoint with the new URL

After editing your script, go to Deploy → Manage deployments → Edit (pencil icon) → change Version to "New version" → Deploy. This updates the same URL with your latest code. Simply saving the script without creating a new version does not affect the deployed web app.

For significant changes (new columns, different field mappings), creating a new deployment and updating Ocriva is the safest approach.

Rows appear but some cells are empty

The field name in extracted.field_name does not match the key in the actual payload. Open the Apps Script Executions log and add a temporary Logger.log(JSON.stringify(data)) line before appendRow to inspect the full incoming payload.


Tips

  • Separate sheets per document type: Create one sheet for invoices, one for receipts, one for contracts. Use a separate Ocriva endpoint per template pointing to each sheet.
  • Use Google Sheets formulas: Once data is flowing in, add a =AVERAGE(G2:G) cell to track average confidence over time, or =COUNTIF(D2:D,"failed") to count failed documents.
  • Conditional formatting: Highlight rows where status is failed in red. Select the Status column → Format → Conditional formatting → "Text is exactly" → failed → red fill.
  • IMPORTRANGE for reporting: Pull the log data into a separate reporting sheet with =IMPORTRANGE("spreadsheet_url", "Document Log!A:G") to keep the raw log clean and the analysis separate.
  • Protect the header row: Right-click row 1 → Protect range → restrict editing to yourself so scripts cannot accidentally overwrite headers if appendRow is ever called with wrong row targeting.