API

A v1 REST API for protecting APK/AAB files with an API key — no browser session required. You can integrate it into your CI and automation pipelines.

  • Base URLhttps://appsolid.net
  • Authentication — an API-key header on every request (Authorization: Bearer or X-API-Key)
  • Content type — request/response bodies are application/json (except the upload PUT)

Authentication#

Issue a key under Settings → API Keys (it is shown only once at creation, so store it somewhere safe). A key can access only its owner's jobs (ownership scoping), and revocation is done from Settings.

# Include one of these headers on every request.
curl -H "Authorization: Bearer ak_live_3f9a8c2e7b1d4056"  ...
curl -H "X-API-Key: ak_live_3f9a8c2e7b1d4056"             ...

Processing flow#

Four steps: upload → protect → download. Files are uploaded directly to storage (never through our server), so there is no size limit.

POST /api/v1/jobs                       create a job, get a presigned upload URL
PUT  {uploadUrl}                        upload the APK/AAB bytes directly to storage
POST /api/v1/jobs/{jobId}/obfuscate     run protection (synchronous)
GET  /api/v1/jobs/{jobId}               poll status, get the download URL

Endpoints#

Create job · POST /api/v1/jobs#

Creates an upload job and returns a presigned PUT URL.

curl -sS -X POST https://appsolid.net/api/v1/jobs \
  -H "Authorization: Bearer $APPSOLID_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "fileName": "app.apk" }'
{
  "jobId": "job_3f9a8c2e",
  "uploadUrl": "https://storage.appsolid.net/in/job_3f9a8c2e/app.apk?X-Amz-Signature=..."
}

Upload file · PUT {uploadUrl}#

Send the raw file bytes to the uploadUrl returned above. No headers beyond the body are required.

curl -sS -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @app.apk

A successful upload returns an empty 200 OK.

Run protection · POST /api/v1/jobs/{jobId}/obfuscate#

Runs synchronously and usually completes within a few seconds. options are opt-in protection flags; when omitted, only core protection is applied.

curl -sS -X POST https://appsolid.net/api/v1/jobs/$JOB_ID/obfuscate \
  -H "Authorization: Bearer $APPSOLID_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "options": ["root", "emulator", "hookFw", "stringEnc", "monitoring"]
      }'
{
  "jobId": "job_3f9a8c2e",
  "status": "COMPLETED",
  "appId": "a1b2c3d4e5f6"
}

Option flagsroot, adb, emulator, hookFw, inlineHook, antiInstrument, timingDebug, stringEnc, selfChecksum, monitoring, and so on. Unknown values return 400. For the meaning of each option, see Protection Options.

⚠️ AAB input constraintselfChecksum is APK-only; if you specify it for an AAB input, the engine rejects it fail-fast. The other options (detection, string encryption, monitoring) work on AAB as well. For background, see Performance · Compatibility.

Check status · GET /api/v1/jobs/{jobId}#

Returns a presigned download URL once complete.

curl -sS https://appsolid.net/api/v1/jobs/$JOB_ID \
  -H "Authorization: Bearer $APPSOLID_KEY"
{
  "status": "COMPLETED",
  "appId": "a1b2c3d4e5f6",
  "downloadUrl": "https://storage.appsolid.net/out/job_3f9a8c2e/app-protected.apk?X-Amz-Signature=..."
}

Full example#

A minimal shell script covering upload through protect, download, and re-signing.

#!/usr/bin/env bash
set -euo pipefail

KEY="ak_live_3f9a8c2e7b1d4056"
BASE="https://appsolid.net"
AUTH=(-H "Authorization: Bearer $KEY")

# 1. create a job
job=$(curl -sS -X POST "$BASE/api/v1/jobs" "${AUTH[@]}" \
        -H "Content-Type: application/json" \
        -d '{ "fileName": "app.apk" }')
jobId=$(echo "$job" | jq -r .jobId)
uploadUrl=$(echo "$job" | jq -r .uploadUrl)

# 2. upload the APK bytes straight to storage
curl -sS -X PUT "$uploadUrl" \
  -H "Content-Type: application/octet-stream" \
  --data-binary @app.apk

# 3. run protection (synchronous)
curl -sS -X POST "$BASE/api/v1/jobs/$jobId/obfuscate" "${AUTH[@]}" \
  -H "Content-Type: application/json" \
  -d '{ "options": ["root", "monitoring"] }'

# 4. fetch the protected (unsigned) output
downloadUrl=$(curl -sS "$BASE/api/v1/jobs/$jobId" "${AUTH[@]}" | jq -r .downloadUrl)
curl -sS "$downloadUrl" -o app-protected.apk

# 5. re-sign with your own keystore before distributing
apksigner sign --ks release.keystore app-protected.apk

Node.js#

The same flow using only the built-in fetch, with no dependencies (Node.js 18+).

import { readFile, writeFile } from "node:fs/promises";

const KEY = "ak_live_3f9a8c2e7b1d4056";
const BASE = "https://appsolid.net";
const auth = { Authorization: `Bearer ${KEY}` };

// 1. create a job
const create = await fetch(`${BASE}/api/v1/jobs`, {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({ fileName: "app.apk" }),
});
const { jobId, uploadUrl } = await create.json();

// 2. upload the APK bytes straight to storage
const bytes = await readFile("app.apk");
await fetch(uploadUrl, {
  method: "PUT",
  headers: { "Content-Type": "application/octet-stream" },
  body: new Blob([bytes]),
});

// 3. run protection (synchronous)
await fetch(`${BASE}/api/v1/jobs/${jobId}/obfuscate`, {
  method: "POST",
  headers: { ...auth, "Content-Type": "application/json" },
  body: JSON.stringify({ options: ["root", "monitoring"] }),
});

// 4. fetch the protected (unsigned) output
const status = await fetch(`${BASE}/api/v1/jobs/${jobId}`, { headers: auth });
const { downloadUrl } = await status.json();
const out = await fetch(downloadUrl);
await writeFile("app-protected.apk", Buffer.from(await out.arrayBuffer()));

// 5. re-sign with your own keystore before distributing (apksigner)

Error responses#

On error the API returns a JSON body of the form { "error": "..." } with the appropriate HTTP status.

StatusMeaning
400Invalid request — unknown option flag or missing fileName
401Missing or invalid API key
403Accessing a job you don't own
404Job not found
429Rate limited
5xxTransient server error
{ "error": "unknown option flag: foo" }

Requests are rate-limited per API key. On a 429, back off and retry.

Notes#

  • The output is zipaligned and unsigned. You must re-sign it with apksigner before distribution (why).
  • The output has a per-job appId baked in, so if you have enabled monitoring, RASP telemetry is linked to the corresponding app in the dashboard.
  • When a threat occurs, you can separately receive a webhook notification that includes an HMAC signature.

Next#