API Documentation

Configure Arguox to POST photo metadata to your own endpoint. No authentication required on Arguox's side — you own the URL, you own the data.

Overview

Arguox sends a single POST request to a user-configured HTTPS URL every time a photo is captured. The request body is application/json and includes all enabled metadata fields, the action type, and the captured image encoded as Base64.

There is no Arguox API key, account, or server involved. You supply the endpoint; Arguox delivers the payload.

Setup

1

Open Settings → API Config

Tap the gear icon on the Camera screen to open Settings, then scroll to the API Config section.

2

Choose a button mode

Select Single for one CAPTURE button or Dual for separate IN and OUT buttons, each with its own endpoint.

3

Enter your endpoint URL(s)

Paste your HTTPS endpoint URL. For Dual mode, set separate URLs for IN and OUT actions.

4

Enable Auto-upload on Capture

When toggled on, Arguox POSTs immediately after every capture. When off, uploads queue for manual retry.

5

Configure metadata fields (optional)

In Settings → Metadata, toggle which fields appear in the payload: Location, Date & Time, Device Info.

Button Modes

Single Mode

CAPTURE

One shutter button. One endpoint. The action field is always "CAPTURE".

Dual Mode

IN / OUT

Two buttons with separate endpoints. The action is "IN" or "OUT".

IN
OUT

Endpoint

POST {your-configured-url}
Content-Type: application/json

Your endpoint must accept a POST request with a JSON body. No special headers are added by Arguox beyond Content-Type.

HTTPS only. Arguox will not send requests to http:// URLs. Your endpoint must serve a valid TLS certificate.

Payload Fields

Only fields that are enabled in Settings appear in the payload. Fields omitted when offline are noted below.

Core (always present)

FieldDescription
action"CAPTURE", "IN", or "OUT" — depends on button mode.
imageBase64The captured image encoded as Base64. Decode and save as JPEG on your server.
imageFileNameSuggested filename, e.g. IMG_20260514_103007.jpg.

Time Toggle in Settings

FieldDescription
timestampISO 8601 UTC timestamp from the device clock.
dateLocal date, e.g. 2026-05-14.
timeLocal time in 12-hour or 24-hour format depending on user setting.
timezoneIANA timezone name, e.g. Etc/UTC.
unixTimestampUnix epoch seconds.

Location Toggle in Settings Omitted offline

FieldDescription
latitudeGPS latitude. Precision configurable in Settings.
longitudeGPS longitude.
streetReverse-geocoded street name via Nominatim.
barangaySub-district / barangay if available.
cityCity or municipality.
provinceProvince, state, or region.
countryFull country name.
countryCodeISO 3166-1 alpha-2 (two-letter country code).
postalCodePostal / ZIP code if available.

Device Info Toggle in Settings

FieldDescription
deviceNameHuman-readable device name.
deviceBrandManufacturer brand, e.g. Samsung.
deviceModelModel identifier, e.g. SM-S921B.
osVersionOS name and version, e.g. Android 15.

Camera & Manual

FieldDescription
cameraType"rear" or "front".
flashStatus"on" or "off".
manualDetailsArray of free-text lines defined in Settings. Empty array if none set.

Environmental Toggle in Settings → Environmental All off by default

Each field is toggled independently. Fields not enabled are omitted from both the overlay and the payload. Weather and humidity require an internet connection (Open-Meteo API); altitude is GPS-derived and works offline.

FieldDescription
altitudeMetres above sea level from the GPS fix. e.g. "12.3 m".
pressureBarometric pressure from the device sensor. e.g. "1013.2 hPa". Omitted on devices without a barometer.
weatherCurrent weather condition from Open-Meteo. e.g. "Partly cloudy". Omitted offline.
humidityRelative humidity from the same Open-Meteo request. e.g. "72%". Omitted offline.

Full Example

Complete payload — all fields enabled, Dual mode, IN action:

{
  "action":        "IN",
  "timestamp":     "2026-05-14T10:30:00Z",
  "date":          "2026-05-14",
  "time":          "10:30:00 AM",
  "timezone":      "Etc/UTC",
  "unixTimestamp": 1747218600,
  "latitude":      "37.7749",
  "longitude":     "-122.4194",
  "street":        "Main Street",
  "barangay":      "Sample District",
  "city":          "Sample City",
  "province":      "Sample Region",
  "country":       "Sample Country",
  "countryCode":   "--",
  "postalCode":    "00000",
  "deviceName":    "Samsung Galaxy S24",
  "deviceBrand":   "Samsung",
  "deviceModel":   "SM-S921B",
  "osVersion":     "Android 15",
  "cameraType":    "rear",
  "flashStatus":   "off",
  "manualDetails": ["Job #1042", "Supervisor: Juan Lobby"],
  "imageBase64":   "BASE64_ENCODED_JPEG_DATA",
  "imageFileName": "IMG_20260514_103007.jpg"
}

Minimal payload (Single mode, offline)

{
  "action":        "CAPTURE",
  "timestamp":     "2026-05-14T10:30:00Z",
  "date":          "2026-05-14",
  "time":          "10:30:00 AM",
  "timezone":      "Etc/UTC",
  "unixTimestamp": 1747218600,
  "manualDetails": [],
  "imageBase64":   "BASE64_ENCODED_JPEG_DATA",
  "imageFileName": "IMG_20260514_103007.jpg"
}

When offline, location fields are omitted entirely. Timestamp, device info, and manual details are still present.

Offline & Upload Queue

Photo capture and local gallery save always succeed regardless of connectivity. The free ad-supported tier still requires internet to serve ads — Remove Ads unlocks full offline use.

When the device is offline at capture time:

  • GPS and reverse-geocoded address are omitted from overlay and payload.
  • Device clock is used for the timestamp (same as when online).
  • The API POST is queued on your device.

When internet reconnects, queued uploads retry automatically. The QUEUE pill shows pending + failed count.

Duplicate prevention: Implement idempotency on your server using imageFileName or a composite of action + unixTimestamp + deviceModel.

Timestamps

Arguox uses the device's own clock to timestamp each capture — there's no external time-verification API and no drift check, so the shutter never waits on a network round-trip.

The device time is written directly to the timestamp value in the payload.

Error Handling

Arguox considers any HTTP 2xx response a success. Any other status code (including 3xx redirects) is treated as a failure and queued for retry.

Retry behavior

  • Automatic retry when internet reconnects.
  • Manual per-item retry via the Queue modal.
  • Batch retry via "Retry Failed" button.
  • Retry count and last retry timestamp stored per item on your device.

Recommended server response

HTTP/1.1 200 OK
Content-Type: application/json

{ "status": "ok" }

Google Sheets & Drive

Use a free Google Apps Script web app as your endpoint. Every capture will save the photo to your Google Drive and log a row in your Google Sheet — no server, no hosting, no cost.

The photo is sent as Base64 inside the JSON body. An 8 MP JPEG at quality 85 is roughly 1–1.5 MB raw, or ~2 MB as Base64 — well within Apps Script's 6 MB request limit.
1

Open Google Apps Script

Go to script.google.com and click New project. Rename it to anything you like, e.g. "Arguox Receiver".

2

Paste the script below

Replace the default code with the complete script shown below. It creates the Drive folder and Sheet automatically on the first request.

3

Deploy as a Web App

Click Deploy → New deployment → Type: Web app. Set Execute as: Me and Who has access: Anyone. Click Deploy and copy the URL.

4

Add the URL to Arguox

In Arguox Capture → Settings → API Config, paste the deployment URL as your endpoint. Enable Auto-upload. Done.

Apps Script code

function doPost(e) {
  try {
    var data = JSON.parse(e.postData.contents);

    // 1. Save photo to Google Drive
    var driveLink = "";
    if (data.imageBase64 && data.imageFileName) {
      var folder = getOrCreateFolder("Arguox Captures");
      var bytes  = Utilities.base64Decode(data.imageBase64);
      var blob   = Utilities.newBlob(bytes, "image/jpeg", data.imageFileName);
      var file   = folder.createFile(blob);
      file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
      driveLink  = file.getUrl();
    }

    // 2. Log row to Google Sheets
    var sheet = getOrCreateSheet("Arguox Logs");
    sheet.appendRow([
      data.date          || "",
      data.time          || "",
      data.action        || "",
      data.latitude      || "",
      data.longitude     || "",
      [data.street, data.city, data.country].filter(Boolean).join(", "),
      [data.deviceBrand, data.deviceModel].filter(Boolean).join(" "),
      data.imageFileName || "",
      driveLink,
      new Date().toISOString(),
    ]);

    return ContentService
      .createTextOutput(JSON.stringify({ status: "ok", driveLink: driveLink }))
      .setMimeType(ContentService.MimeType.JSON);

  } catch (err) {
    return ContentService
      .createTextOutput(JSON.stringify({ status: "error", message: err.message }))
      .setMimeType(ContentService.MimeType.JSON);
  }
}

// ── helpers ──────────────────────────────────────────────────────────────────

function getOrCreateFolder(name) {
  var folders = DriveApp.getFoldersByName(name);
  return folders.hasNext() ? folders.next() : DriveApp.createFolder(name);
}

function getOrCreateSheet(name) {
  var files = DriveApp.getFilesByName(name);
  var ss;
  if (files.hasNext()) {
    ss = SpreadsheetApp.open(files.next());
  } else {
    ss = SpreadsheetApp.create(name);
    var sheet = ss.getActiveSheet();
    sheet.setName("Logs");
    sheet.appendRow([
      "Date", "Time", "Action",
      "Latitude", "Longitude", "Address",
      "Device", "Filename", "Drive Link", "Received At"
    ]);
    sheet.setFrozenRows(1);
  }
  return ss.getActiveSheet();
}

What you get

Google Drive

Arguox Captures /

IMG_20260519_093012.jpg

IMG_20260519_093847.jpg

IMG_20260519_102201.jpg

One file per capture, auto-organized in a folder.

Google Sheets — Arguox Logs

DateTimeActionAddressFilename
2026-05-1909:30CAPTURESite St, Location CityIMG_093012.jpg
2026-05-1909:38INSite St, Location CityIMG_093847.jpg
2026-05-1910:22OUTSite St, Location CityIMG_102201.jpg

One row per capture with Drive link included.

Each Apps Script deployment URL is unique to your Google account. Do not share it publicly — anyone with the URL can POST to it. Treat it like a password.