Skip to main content

API documentation

How to send each photo and its metadata from Arguox Capture to an HTTPS endpoint you control.

Overview

When you tap Upload on a photo, Arguox sends one POST request to the HTTPS URL you set. The body is application/json and holds the action type, every metadata field you have turned on, and the image as Base64.

There's no Arguox API key, account or server in between. You provide the endpoint and the app posts to it directly.

Setup

1

Open API Config

Tap the gear on the camera screen to open Settings, then scroll down to API Config.

2

Choose a button mode

Pick Single for one CAPTURE button, or Dual for separate IN and OUT buttons. In Dual mode each button gets its own endpoint.

3

Enter your endpoint URL

Paste your HTTPS URL. In Dual mode, fill in one URL for IN and one for OUT.

4

Take a photo and tap Upload

After each capture the preview screen shows an Upload button. Tap it to post the photo. If you are offline or the request fails, the upload stays in the queue and retries later.

5

Choose your metadata fields (optional)

Under Metadata in Settings, turn Location, Date & Time and Device Info on or off. Fields you turn off are left out of the payload.

Button modes

Single mode

CAPTURE

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

Dual mode

IN / OUT

Two buttons, each with its own endpoint. The action field is "IN" or "OUT".

IN
OUT

Endpoint

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

Your endpoint needs to accept a POST request with a JSON body. Arguox doesn't add any custom headers besides Content-Type.

HTTPS only. Arguox won't post to http:// URLs, and your endpoint needs a valid TLS certificate.

Payload fields

The payload only includes fields you have turned on in Settings. Fields that are dropped when you're offline are marked below.

Core (always present)

FieldDescription
action"CAPTURE", "IN", or "OUT", depending on the button mode.
imageBase64The photo as a Base64-encoded JPEG. Decode it and save it as a .jpg 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 your setting.
timezoneIANA timezone name, e.g. Etc/UTC.
unixTimestampUnix epoch seconds.

Location Toggle in Settings Omitted offline

FieldDescription
latitudeGPS latitude. You can set the precision in Settings.
longitudeGPS longitude.
streetStreet name, looked up from the coordinates with Nominatim.
barangayBarangay or sub-district, if available.
cityCity or municipality.
provinceProvince, state, or region.
countryFull country name.
countryCodeISO 3166-1 alpha-2 (two-letter country code).
postalCodePostal or ZIP code, if available.

Device Info Toggle in Settings

FieldDescription
deviceNameThe device name as people would read it.
deviceBrandManufacturer brand, e.g. Samsung.
deviceModelModel identifier, e.g. SM-S921B.
osVersionOS name and version, e.g. Android 15.

Camera and manual details

FieldDescription
cameraType"rear" or "front".
flashStatus"on" or "off".
manualDetailsThe free-text lines you set in Settings. An empty array if there are none.

Environmental Toggle under Environmental in Settings All off by default

Each field has its own toggle. Anything you leave off is missing from both the overlay and the payload. Altitude comes from the GPS fix and works offline. Elevation, weather and humidity come from the Open-Meteo API, as does pressure on Android, so they're left out when there's no connection.

FieldDescription
altitudeMetres above sea level from the GPS fix, e.g. "12.3 m".
elevationGround elevation at your coordinates from Open-Meteo, e.g. "45 m". Android only; omitted offline.
pressureAir pressure, e.g. "1013.2 hPa". On iOS it's read from the phone's barometer, and the toggle is greyed out on phones without one. On Android it comes from Open-Meteo and is left out offline.
weatherCurrent weather from Open-Meteo, e.g. "Partly cloudy". Omitted offline.
humidityRelative humidity from the same Open-Meteo request, e.g. "72%". Omitted offline.

Full example

A payload from the IN button in Dual mode, with Date & Time, Location and Device Info turned on:

{
  "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"
}

Offline, the location fields are left out. The time, device info and manual details are still sent if you have them turned on.

Offline and the upload queue

Taking photos and saving them to the gallery don't depend on a connection. The free version does need internet, because it has to load ads. With Remove Ads the app works fully offline.

When the phone is offline at capture time:

  • GPS and the street address are left off the overlay and the payload.
  • The time comes from the phone's clock, same as when online.
  • If you tap Upload, the request waits in a queue on the phone.

When the connection comes back, queued uploads go out on their own. The QUEUE pill on the camera shows how many are pending or failed.

Duplicates: make your endpoint idempotent. Key on imageFileName, or on action + unixTimestamp + deviceModel together.

Timestamps

Arguox stamps each capture with the phone's own clock. It doesn't check the time against a server, so the shutter never waits on the network. If the phone's clock is wrong, the timestamp will be wrong too.

That time goes straight into the timestamp field in the payload.

Error handling

Any HTTP 2xx response counts as a success. Any other status, 3xx redirects included, counts as a failure and goes back in the queue for a retry.

How retries work

  • Queued uploads retry on their own when the connection comes back.
  • You can retry a single item from the queue.
  • "Retry Failed" retries every failed item at once.
  • Each item keeps its retry count and last retry time on the phone.

Recommended server response

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

{ "status": "ok" }

Google Sheets & Drive

You can use a free Google Apps Script web app as your endpoint. Each upload saves the photo to your Google Drive and adds a row to a Google Sheet. You don't need a server or hosting, and it costs nothing.

The photo travels as Base64 inside the JSON body. An 8 MP JPEG at quality 85 is about 1 to 1.5 MB, or around 2 MB once encoded. Apps Script accepts requests up to 6 MB, so there's plenty of room.
1

Open Google Apps Script

Go to script.google.com and click New project. Name it whatever you like, for example "Arguox Receiver".

2

Paste the script below

Delete the default code and paste in the script below. It creates the Drive folder and the Sheet the first time an upload comes in.

3

Deploy it as a web app

Click Deploy, then New deployment, and choose Web app as the type. Set Execute as to Me and Who has access to Anyone. Click Deploy and copy the URL it gives you.

4

Add the URL to Arguox

In Arguox Capture, open Settings and go to API Config. Paste the deployment URL as your endpoint. Your next upload will show up in Drive and Sheets.

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 upload, all in one 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 upload, with a link to the photo in Drive.

Your deployment URL is tied to your Google account, and anyone who has it can post to your script. Keep it private, the same way you would a password.