| ← Back to s.plings.io | Core Systems |
URL Structure & Parameters
Created: Tue 29 Jul 2025 06:49:39 CEST
Updated: Wed 06 May 2026 17:14:18 CEST - Phase 1 naming migration: clarified transport-agnostic URL format (QR / NFC / RFID); resolved native-app integration architecture (apps call GraphQL API directly; Gateway handles browser path)
Document Version: 1.2 - Transport naming and native-app architecture resolution
Security Classification: Public Technical Documentation
Target Audience: Frontend Developers, Backend Developers
Author: Paul Wisén
Overview
This document details the URL structure and parameter specifications for the s.plings.io Gateway service. The same URL format is used for every scan transport — QR codes, NFC tags, RFID tags, and (future) Bluetooth beacons all encode this identical URL.
Incoming Scan URL Format
Base Structure
https://s.plings.io?t=<transport>&i=<instance>&p=<path>&cp=<class_pointer>
Core Parameters
| Parameter | Name | Required | Description | Valid Values | Example |
|---|---|---|---|---|---|
t |
Transport | Yes | Scan transport | q, n, r, b |
q (QR code) |
i |
Instance Key | Yes | Unique instance identifier | Base58 string | 4kyQCd5tMDjJVWJH5h95gUcjq3qTX2cj5nwjVyqBRwLo |
p |
Path | Yes | HD wallet derivation path | Dot-separated integers | 4.2.3.3.6 |
cp |
Class Pointer | No | Cryptographic class identifier | Base58 string | 4K7mX9abDcE |
Extended Parameters (Future)
| Parameter | Name | Description | Example |
|---|---|---|---|
tx |
Transaction | Transaction type | buy, rent, service |
svc |
Service | Service identifier | repair, insurance |
price |
Price | Amount in cents | 2500 |
dur |
Duration | Time period | 1d, 1w, 1m |
Path Structure Breakdown
The path parameter (p) follows the HD wallet hierarchy:
p=4.2.3.3.6
│ │ │ │ └── Instance ID
│ │ │ └──── Batch/Series
│ │ └────── Category
│ └──────── Plant/Location
└────────── Manufacturer ID
Outgoing Redirect Parameters
After processing, s.plings.io enriches the URL with additional parameters:
Standard Redirect Parameters
| Parameter | Name | When Included | Description | Example |
|---|---|---|---|---|
oid |
Object ID | When object exists | Database UUID for object | obj-550e8400-e29b-41d4 |
ikey |
Instance Key | Always | Original scanned identifier | 4kyQCd5tMDjJVWJH5h95gUcjq3qTX2cj5nwjVyqBRwLo |
path |
HD Path | Always | Base58-encoded wallet path | 4.2.3.3.6 |
class |
Class ID | When resolved | Class UUID | 550e8400-e29b-41d4-a716-446655440000 |
cptr |
Class Pointer | When available | Cryptographic class pointer | 4K7mX9abDcE |
src |
Source | Always | Tracking source | scan |
loc |
Location | With consent | GPS coordinates | 59.3293,18.0686 |
ts |
Timestamp | Optional | Scan timestamp | 1699123456789 |
URL Transformation Examples
Example 1: Known Object for Sale
Input:
https://s.plings.io?t=q&i=4kyQCd5tMDjJVWJH5h95gUcjq3qTX2cj5nwjVyqBRwLo&p=4.2.3.3.6
Processing:
- Identifier verified ✓
- Object found in database
- Status: FOR_SALE
- Route to marketplace
Output:
https://market.plings.io/item?oid=obj-123&ikey=4kyQCd5tMDjJVWJH5h95gUcjq3qTX2cj5nwjVyqBRwLo&path=4.2.3.3.6&src=scan
Example 2: Unknown Generic Tag
Input:
https://s.plings.io?t=q&i=7mnPQd8uNEkKWXKI6i06hVdjr4rUY3dk6oxkWyrCSwMp&p=1.1.1.1.1
Processing:
- Identifier verified ✓
- No object found
- Generic Plings tag
- Route to creation flow
Output:
https://plings.io/create?ikey=7mnPQd8uNEkKWXKI6i06hVdjr4rUY3dk6oxkWyrCSwMp&path=1.1.1.1.1&cptr=4K7mX9&src=scan
Example 3: Manufacturer Pre-printed Tag
Input:
https://s.plings.io?t=q&i=9abCDe3vOPqRStUvWxYz&p=17.3.42.1.1001&cp=3K7mX9
Processing:
- Class pointer verified ✓
- No object instance yet
- IKEA product tag
- Route to claim flow
Output:
https://plings.io/claim?ikey=9abCDe3vOPqRStUvWxYz&path=17.3.42.1.1001&class=550e8400-e29b-41d4-a716-446655440000&cptr=3K7mX9&src=scan
Service-Specific Routing
The Gateway routes to different services based on object status:
| Object Status | Target Service | Example URL |
|---|---|---|
| FOR_SALE | market.plings.io | /item?oid=... |
| FOR_RENT | rent.plings.io | /listing?oid=... |
| LENDABLE | lend.plings.io | /borrow?oid=... |
| LOST | plings.io | /found?oid=... |
| NORMAL | plings.io | /o/{oid}?... |
| UNKNOWN | plings.io | /create?ikey=... |
Parameter Validation Rules
Instance Key (i)
- Must be valid Base58 string
- Length: 32-48 characters
- No special characters except Base58 alphabet
Path (p)
- Dot-separated integers only
- Maximum 10 levels deep
- Each segment: 1-999999
- Example:
1.2.3.4.5
Transport (t)
- Enum:
q(QR),n(NFC),r(RFID),b(Bluetooth — future) - Case sensitive
- Default:
qif missing - The Gateway’s routing logic does not branch on transport — this parameter exists for analytics and rare transport-specific edge cases only
Class Pointer (cp)
- Optional but recommended for manufacturer tags
- Base58 encoded
- Used for offline verification
Error Handling
Invalid parameters result in redirects to error pages:
https://plings.io/scan-error?reason=invalid&details=bad_instance_key
https://plings.io/scan-error?reason=rate_limit&retry_after=60
https://plings.io/scan-error?reason=system&code=500
Security Considerations
- All parameters are validated and sanitized
- SQL injection prevention on all inputs
- XSS protection through proper encoding
- Rate limiting applied per parameter combination
- Invalid UTF-8 sequences rejected
Native App Integration
Native apps (Plings-iOS, Plings-Android — planned) bypass the Gateway and call the GraphQL API directly. This is intentional: native NFC interactions need to be instant, and a network roundtrip to s.plings.io purely for redirection would defeat the point.
The unified contract is the GraphQL API at api.plings.io, not the Gateway’s HTTP front door. The Gateway exists for entry points that don’t have a native app to do their own resolution (browser, e-mail link, OS camera).
Native scan flow
- User taps an NFC tag (or scans a QR) inside the native app
- The app parses the URL parameters from the scan payload — same parameters documented above
- The app calls a GraphQL query (e.g.,
resolveIdentifier) with the parsedi/p/cp/tvalues - The app routes the user locally based on the response (object data, status, action hints)
- The app reports a scan event back to the API for centralized analytics
// Native scan path — call GraphQL directly
const { data } = await api.resolveIdentifier({
instanceKey: params.i,
path: params.p,
classPointer: params.cp,
transport: params.t,
});
// Then act on data.object.status / data.routing locally
When the Gateway still handles a scan from a phone with the app installed
When a s.plings.io URL is opened from outside the app (a friend shares the link, the user scans with the OS camera and chooses the browser, etc.), the OS hands the URL to whichever app is registered. If the Plings app is registered as a universal/app link handler, it can intercept and run the native flow. Otherwise the browser hits s.plings.io and the Gateway routes normally.
Where centralized concerns live
Logging, rate limiting, validation, and authorization are not the Gateway’s exclusive responsibility — they live behind the GraphQL API and apply to every caller, native or web. The Gateway adds an extra firewall layer specifically for browser-side abuse (DDoS, automated scrapers); native apps can’t reach the API without their own credentials and rate limits.