EdgeNFC
Sign in Start free

Hosted Gateway — Option A#

The Hosted Gateway is the zero-code path. EdgeNFC holds your keys (envelope-encrypted), verifies every tap, and serves the result page at edgenfc.com. You provision tags once with the free Android app; after that, a tap just works. No servers, no code.

This guide takes you from a fresh signup to a genuine tap returning { "authentic": true, … } and a forged tap returning { "authentic": false, … }.

Prerequisites#

  • An NFC-capable Android phone with the free EdgeNFC provisioning app (Option C).
  • One or more blank NTAG 424 DNA tags.
  • A terminal with curl (the API calls below are plain HTTPS/JSON).

You do not need a credit card. The Sandbox plan is free forever and includes 100 active tags — enough to build and test the whole flow.

Step 1 — Sign up (Sandbox, no card)#

Create an account. Signup is email-only and provisions the free Sandbox plan automatically.

curl -X POST https://edgenfc.com/api/signup \
  -H "content-type: application/json" \
  -d '{ "email": "you@example.com" }'

Response — save the token; every management call below sends it as a bearer token:

{ "user_id": "usr_01H…", "token": "eyJ…" }
export TOKEN="eyJ…"   # paste the token from the response

Note

Sandbox is signup-without-card: plan: "sandbox", status: "active", a 100 active-tag limit, and unlimited scans. You can upgrade later (see Plans) without redoing any of these steps.

Step 2 — Create a hosted System#

A System is a customer/tenant boundary with its own System Master Key. Create one in the hosted custody tier. Ask for the key to be revealed once so the provisioning app can write tags; the server keeps its own envelope-encrypted copy for the verify path.

curl -X POST https://edgenfc.com/api/keys \
  -H "authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d '{ "tier": "hosted", "reveal": true }'

Response — the key_id is your System ID; the key is shown this one time only:

{ "key_id": "sys_01H…",
  "tier": "hosted",
  "key": "00112233445566778899aabbccddeeff",
  "key_version": 1,
  "warning": "shown once; server keeps an envelope-encrypted copy" }

Warning

The key above is a test key, not a real one — never paste a production key into docs, tickets, or chats. Your real key is shown exactly once at creation and is never returned again. If you lose it, rotate the System (POST /api/keys/{id}/rotate, body {"confirm": true}) to issue a new version, then re-provision. Rotation opens a 90-day window in which the previous version still verifies, so the re-write is a scheduled pass — see Key management → Rotation.

Note

Draft: the once-only hosted reveal parameter is the documented hosted-custody design (the app needs the key to write tags; the server retains the encrypted copy to verify). It is still being wired into the Sandbox build — track the changelog. Without reveal, hosted key creation returns the key_id only and never the key bytes.

Step 3 — Provision tags with the Android app#

Open the EdgeNFC app and enter three things:

  • the System Master Key (the 32-hex key from Step 2),
  • the System ID (key_id, e.g. sys_01H…),
  • the host (edgenfc.com).

Tap Arm, then hold a blank NTAG 424 DNA to the phone. The app runs the full provisioning sequence (authenticate, diversify a per-tag key, ChangeKey, write the SUN URL) and reads the tag back, showing SELF-VERIFY ✓. Repeat for each tag, or use batch mode. Full details are in the Android provisioning guide.

When you finish, tap Export registry in the app — it produces a JSON file describing the tags you just wrote (UIDs, key version, SDM config). No key bytes are ever in the export.

Step 4 — Import the registry#

Import the exported registry so the gateway recognises your tags:

curl -X POST https://edgenfc.com/api/tags/import \
  -H "authorization: Bearer $TOKEN" \
  -H "content-type: application/json" \
  -d @registry.json

Response:

{ "imported": 12 }

Import is idempotent by UID, so re-importing the same batch is safe. If an import would push you past your plan's active-tag limit, it returns 402 with { "error": { "code": "quota_exceeded" } } instead — upgrade, or free up tags, and retry.

Step 5 — Tap to verify#

Tapping a provisioned tag opens its SUN URL, which the tag itself fills in with its UID, a monotonic read counter, and a fresh MAC. The gateway's public endpoint verifies it:

GET https://edgenfc.com/verify?sys=sys_01H…&uid=04a1b2c3d4e580&ctr=000042&mac=…

Verify it worked#

A genuine tap returns 200 with authentic: true and the advanced counter:

{ "authentic": true, "uid": "04a1b2c3d4e580", "read_ctr": 42,
  "replay": "ok", "first_seen": false, "verified_at": "2026-08-01T12:00:00Z" }

A forged or tampered tap returns 200 with authentic: false and a machine-readable reason:

{ "authentic": false, "reason": "mac_mismatch" }

The reason is one of mac_mismatch, malformed, non_monotonic, or unknown_uid.

Important

A forgery is a valid request with a false result, so it returns 200 with authentic: false — never a 4xx. Verification is a fact about the tap, not a request error. Tap the same URL twice and the second read comes back non_monotonic: proof the monotonic counter is stopping replays.

You now have a hosted verification loop end to end. On tap, a real tag shows Genuine ✓; a clone or a hand-edited URL shows Not verified.

Plans and upgrading#

Sandbox is enough to ship a pilot. Upgrade when you need more active tags or paid features:

PlanPrice (USD)Active tagsKey features
Sandbox$0 forever100standard routing, basic API
Creator$19/mo2,500custom dynamic URL routing
Brand$69/mo25,000custom domain, advanced analytics, webhooks
Enterprise$299/mo100,000management API, multi-user

Scans are unlimited on every plan. Every tier above, Enterprise included, is a flat published price you buy yourself — there is no sales call and no quote. What is genuinely negotiated is what sits beyond the table: more than 100,000 active tags, a custom SLA, or an on-premise / air-gapped deployment. Upgrades are self-service through Stripe-hosted Checkout — card data never touches EdgeNFC. Downgrading never deletes tags or breaks tags already in the field; it only blocks new provisioning above the lower limit.

Next steps#