Skip to main content
sApp

sApp

The SDK for building on Superroute — on phones, the web, the desktop and the server:

  • Apps and websites for customers and drivers that run for one or more service providers, with ready-made UI components on Flutter, the web and React, SwiftUI, Jetpack Compose, React Native, WPF and WinForms.
  • Desktop programs on Windows and macOS, with the same calls and components.
  • Back-end services and integrations in JavaScript/Node, Python, PHP, Go, Java and C#: the partner app API and the Open Platform capabilities such as routing, geocoding and ETA.
  • Webhook receivers in every language: Superroute's signatures checked, replays refused, events parsed.

One client per language, generated from the same API description, so every platform calls Superroute the same way. Superroute SDK License — see LICENSE and open.superroute.ca/developer/license.

Documentation: open.superroute.ca/developer/sapp. Swift packages, the Maven repository, the Go module and the example apps: Goodneo-Solutions/sapp-releases. PHP: Goodneo-Solutions/sapp-php.

Package Platform Status
@superrouteca/sapp JavaScript and TypeScript: web, Node 20+, React Native 0.2.5
@superrouteca/sapp-react React: provider and hooks 0.2.5
@superrouteca/sapp-react-native React Native: hooks, secure token store 0.2.5
sapp Dart and Flutter 0.2.5
SApp (Swift Package Manager) iOS 15+, macOS 12+ 0.2.5
ca.superroute:sapp / sapp-android (Maven) Kotlin on the JVM and Android 7+ 0.2.5
Superroute.SApp (NuGet) .NET 8: Windows apps and servers 0.2.5
superroute-sapp (PyPI, import sapp) Python 3.9+: servers and scripts 0.2.5
superroute/sapp (Packagist) PHP 8.1+: servers 0.2.5
github.com/Goodneo-Solutions/sapp-releases/go Go 1.21+: servers 0.2.5
ca.superroute:sapp-java (Maven) Java 17+: servers, no dependencies 0.2.5
sapp_ui Flutter UI kit: sign-in, notifications, wallet, orders, pickups 0.2.5
@superrouteca/sapp-ui Web UI kit: the same components as custom elements 0.2.5
@superrouteca/sapp-react/ui React components over the web UI kit 0.2.5
SAppUI (Swift Package Manager) SwiftUI UI kit: iOS 15+, macOS 12+ 0.2.5
ca.superroute:sapp-ui-android (Maven) Jetpack Compose UI kit: Android 7+ 0.2.5
@superrouteca/sapp-react-native/ui React Native UI kit 0.2.5
Superroute.SApp.Wpf / Superroute.SApp.WinForms (NuGet) WPF and WinForms UI kits over Superroute.SApp.UI 0.2.5

Every package of a release carries the same version. Version numbers never contain the digits 3 or 4.

Profiles

  • Partner — an app registered on the Open Platform console (open.superroute.ca/open-api/apps). It acts for one person at one service provider, with that person's sign-in and the capabilities the app was approved for and the provider switched on.
  • Open — Open Platform capabilities (open.superroute.ca/v1). Inside an app, pass getToken, answered by your own backend with a short-lived token; an Open Platform API key is a server secret and never belongs in an app.

What a caller may do is decided by the server, never by the package installed.

JavaScript

import { createPartnerClient } from '@superrouteca/sapp';

const sr = createPartnerClient({
  appCode: 'p_acme_parcels',
  clientCode: 'k3m9q2',          // the service provider's public client code
  appKey: 'sp_appkey_…',         // restricted key for calls before sign-in
  tokenStore,                    // your secure storage: { get, set, clear }
  locale: 'en',
});

// Sign in with Google or Apple in the app…
await sr.auth.signInWithIdToken(googleIdToken);
// …or on the Superroute sign-in page:
const { url, verifier } = await sr.auth.authorizationUrl({ redirectUri: 'com.acme.parcels:/oauth' });
// open `url` in the system browser, then
await sr.auth.exchangeCode({ code, redirectUri: 'com.acme.parcels:/oauth', verifier });

const notifications = await sr.api.platformAppNotifications();
const me = await sr.graphql('{ partnerAppMe { app_code user_name } }');

React and React Native

import { SuperrouteProvider, useApi, useSession, createSecureTokenStore } from '@superrouteca/sapp-react-native';

const sr = createPartnerClient({ appCode: 'p_acme_parcels', clientCode, appKey, tokenStore: createSecureTokenStore(secureStorage) });

function Inbox() {
  const { signedIn } = useSession();
  const { data, loading, error, reload } = useApi((api) => api.platformAppNotifications(), [], { enabled: signedIn === true });
  …
}

<SuperrouteProvider client={sr}><Inbox /></SuperrouteProvider>

Sign-in on the Superroute page from a phone: sr.auth.signInWithBrowser({ redirectUri, open }), where open(url, redirectUri) shows the page in an in-app browser session and answers the URL it returned to (null when closed). sr.auth.subscribe(fn) is told when a session starts and ends.

Dart and Flutter

import 'package:sapp/sapp.dart';

final sr = PartnerClient(appCode: 'p_acme_parcels', clientCode: 'k3m9q2', appKey: 'sp_appkey_…', tokenStore: store);
await sr.signInWithIdToken(googleIdToken);
final notifications = await sr.api.platformAppNotifications();

Swift

// Package.swift
.package(url: "https://github.com/Goodneo-Solutions/sapp-releases", from: "0.2.5")
// in the target: .product(name: "SApp", package: "sapp-releases"), and .product(name: "SAppUI", package: "sapp-releases") for the UI kit

In Xcode: File > Add Package Dependencies…, https://github.com/Goodneo-Solutions/sapp-releases.

import SApp

let sr = PartnerClient(appCode: "p_acme_parcels", clientCode: "k3m9q2", appKey: "sp_appkey_…", tokenStore: KeychainTokenStore())
try await sr.signIn(idToken: googleIdToken)
let notifications = try await sr.api.platformAppNotifications()

// The Superroute sign-in page in an ASWebAuthenticationSession:
let request = try await sr.authorizationRequest(redirectURI: "com.acme.parcels:/oauth")
// … present request.url, then with the URL the session returned:
try await sr.completeSignIn(request, returnedURL: callbackURL)

Answers are JSON values (answer["data"]?["items"]?.array). onSessionChange is told when a session starts and ends.

Kotlin and Android

// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://goodneo-solutions.github.io/sapp-releases/maven") }
    }
}

// build.gradle.kts: ca.superroute:sapp on the JVM, sapp-android on Android, sapp-ui-android for the Compose kit
implementation("ca.superroute:sapp-android:0.2.5")
implementation("ca.superroute:sapp-ui-android:0.2.5")
val sr = PartnerClient(appCode = "p_acme_parcels", clientCode = "k3m9q2", appKey = "sp_appkey_…", tokenStore = KeystoreTokenStore(context))
sr.signInWithIdToken(googleIdToken)
val notifications = sr.api.platformAppNotifications()   // a kotlinx.serialization JsonElement

The sign-in page: open sr.authorizationRequest(redirectUri).url in a Custom Tab, then sr.completeSignIn(request, returnedUrl). KeystoreTokenStore encrypts the tokens with a key held in the Android Keystore.

C# and .NET

using Superroute.SApp;

using var sr = new PartnerClient("p_acme_parcels", clientCode: "k3m9q2", appKey: "sp_appkey_…", tokenStore: new ProtectedDataTokenStore());
await sr.SignInWithIdTokenAsync(googleIdToken);
var notifications = await sr.Api.PlatformAppNotificationsAsync();   // a System.Text.Json JsonNode

ProtectedDataTokenStore keeps the tokens encrypted for the current Windows user. OnSessionChange is told when a session starts and ends.

Python

from sapp import PartnerClient, OpenClient

sr = PartnerClient('p_acme_parcels', client_code='k3m9q2', app_key='sp_appkey_…')
sr.sign_in_with_id_token(google_id_token)
notifications = sr.api.platform_app_notifications()

open = OpenClient(api_key=os.environ['SUPERROUTE_OPEN_KEY'])   # on a server
place = open.api.geo_forward(body={'address': '100 King St W, Toronto'})

Methods and arguments are snake_case (platform_app_mark_notification_read(notification_id=…)); answers are the parsed JSON. The client is safe to share between threads: concurrent calls share one refresh.

PHP

use Superroute\SApp\PartnerClient;
use Superroute\SApp\OpenClient;

$sr = new PartnerClient('p_acme_parcels', clientCode: 'k3m9q2', appKey: 'sp_appkey_…');
$sr->signInWithIdToken($googleIdToken);
$notifications = $sr->api->platformAppNotifications();

$open = new OpenClient(apiKey: getenv('SUPERROUTE_OPEN_KEY'));
$place = $open->api->geoForward(['address' => '100 King St W, Toronto']);

composer require superroute/sapp. Optional parameters are named arguments; answers are decoded JSON arrays. Requests go through curl when it is loaded, else PHP streams; pass your own Transport to use another HTTP client.

Go

import sapp "github.com/Goodneo-Solutions/sapp-releases/go"

sr, err := sapp.NewPartnerClient(sapp.PartnerOptions{AppCode: "p_acme_parcels", ClientCode: "k3m9q2", AppKey: "sp_appkey_…"})
_, err = sr.SignInWithIDToken(ctx, googleIDToken, nil)
notifications, err := sr.API.PlatformAppNotifications(ctx, sapp.PlatformAppNotificationsParams{})

open, err := sapp.NewOpenClient(sapp.OpenOptions{APIKey: os.Getenv("SUPERROUTE_OPEN_KEY")})
place, err := open.API.GeoForward(ctx, sapp.GeoForwardParams{Body: map[string]any{"address": "100 King St W, Toronto"}})

Each operation takes its parameters in a struct; optional ones are pointers (sapp.String("ios"), sapp.Bool(true)). Answers decode into any (numbers as float64). Failures are *sapp.Error.

Java

// build.gradle.kts: repositories { maven("https://goodneo-solutions.github.io/sapp-releases/maven") }
implementation("ca.superroute:sapp-java:0.2.5")

PartnerClient sr = PartnerClient.builder("p_acme_parcels").clientCode("k3m9q2").appKey("sp_appkey_…").build();
sr.signInWithIdToken(googleIdToken);
Object notifications = sr.api.platformAppNotifications();

OpenClient open = OpenClient.builder().apiKey(System.getenv("SUPERROUTE_OPEN_KEY")).build();
Object place = open.api.geoForward(Map.of("address", "100 King St W, Toronto"));

Required parameters are arguments; optional ones, an optional body and files go in Args (api.x(Args.of().with("page", 2))). The artifact has no dependencies: answers are plain Map, List, String, Long, Double, Boolean or null (the built-in Json reads and writes them), so it never conflicts with the Jackson or Gson version a server already carries.

Webhooks

Every language verifies Superroute webhooks: the signature over the raw body, a timestamp inside the tolerance (300 seconds) so an old request cannot be replayed, and the event parsed from the native body or a CloudEvents envelope.

import { verifyWebhook } from '@superrouteca/sapp';   // also from sapp-react and sapp-react-native

const event = await verifyWebhook(rawBody, req.headers, process.env.SUPERROUTE_WEBHOOK_SECRET);
if (await alreadyHandled(event.id)) return;            // an event can arrive more than once
Language Call
Dart verifyWebhook(body, headers, [secret])
Swift try Webhooks.verify(body:headers:secrets:)
Kotlin Webhooks.verify(body, headers, listOf(secret))
C# Webhooks.Verify(body, headers, new[] { secret })
Python verify_webhook(body, headers, secret)
PHP Webhooks::verify($body, $headers, $secret)
Go sapp.VerifyWebhook(body, r.Header, []string{secret}, sapp.WebhookOptions{})
Java Webhooks.verify(body, headers, secret, WebhookOptions.defaults())

Pass the body exactly as received, never a re-encoded object. The secret is the endpoint's signing secret, as whsec_… or plain; during a key rotation pass both. The scheme is the way the endpoint signs:

Scheme Sender Headers
standard (default) any endpoint with Standard Webhooks switched on webhook-id, webhook-timestamp, webhook-signature
tenant order, tracking, driver and device-order webhooks X-Webhook-Signature-V2, X-Webhook-Timestamp
partner_locker, tpd partner locker deliveries, third-party delivery providers X-Webhook-Signature: v1=…, X-Webhook-Delivery-Id, X-Webhook-Timestamp
loading_plan loading plan callbacks X-Webhook-Signature: v1=…, X-Webhook-Timestamp
open_platform Open Platform job callbacks X-Open-Signature, X-Open-Timestamp
dataset dataset webhooks X-Webhook-Signature (the timestamp is not signed)

Tenant webhooks keep the time of the first try on their retries, so their tolerance is 12,600 seconds. The event carries id, type (without the CloudEvents ca.superroute. prefix), time, subject, source, business, attempt, format (native, ce_binary, ce_structured) and data. A failure is a WebhookVerificationError / WebhookVerificationException with a code: missing_header, invalid_timestamp, timestamp_out_of_tolerance, invalid_signature, invalid_secret, invalid_payload or unknown_scheme. The test vectors are in testdata/webhooks/vectors.json (tools/webhook_vectors.py).

Flutter UI kit

SuperrouteScope(
  client: sr,
  theme: ThemeTokens.fromBranding(bootstrap['client']['settings']['branding']),
  onSignInRequired: () => router.go('/sign-in'),
  child: SrPickupList(onShowCode: (value) => showQr(value)),
)

Components, for customer apps: SrSignIn, SrNotificationInbox, SrWalletSummary, SrOrderList, SrPickupList, SrOrderDetail, SrTracking, SrNotificationPreferences, SrAccountPrivacy, SrLockerMap (with SrSMapAdapter(controller) for the app's sMap map), SrReturnList, SrReturnForm, SrShippingOrderForm, the store and mall (SrStoreDirectory, SrStorePage, SrProductDetail, SrCart, SrCheckout) and group buy (SrGroupBuyList, SrGroupBuy, SrGroupBuyOrders, SrGroupBuyOrder, and for group leaders SrGroupBuyLead, SrGroupBuyLeading, SrGroupBuyLeadingGroup); for driver apps: SrDriverRoutes, SrRouteStops, SrStop (photos, a signature pad, the delivery PIN), SrDriverDuty, SrDriverPool, SrRouteBoards, SrRouteBoard, SrBoardClaims, SrDriverEarnings, SrScanLookup. Files, the position, the scanner and card payments are the app's: a component takes a callback for them (pickFile, pickPhoto, getPosition, scan) or hands the payment or a link to share to the app (onPayment, onShare). They raise events and never navigate; what each shows, its states and strings are in the UI kit reference, the same on every platform. Strings in 14 languages live in ui/strings/ (tools/ui_strings.py).

Web and React UI kit

<script type="module">
  import { configure } from '@superrouteca/sapp-ui';
  configure({ client: sr, theme: themeFromBranding(branding), locale: 'en' });
</script>
<sr-pickup-list></sr-pickup-list>

Elements: sr-sign-in, sr-notification-inbox, sr-wallet-summary, sr-order-list, sr-pickup-list, sr-order-detail, sr-tracking, sr-notification-preferences, sr-account-privacy, sr-locker-map (map = smapAdapter(sMapMap)), sr-return-list, sr-return-form, sr-shipping-order-form, sr-store-directory, sr-store-page, sr-product-detail, sr-cart, sr-checkout, sr-group-buy-list, sr-group-buy, sr-group-buy-orders, sr-group-buy-order, for group leaders sr-group-buy-lead, sr-group-buy-leading, sr-group-buy-leading-group, and for driver apps sr-driver-routes, sr-route-stops, sr-stop, sr-driver-duty, sr-driver-pool, sr-route-boards, sr-route-board, sr-board-claims, sr-driver-earnings, sr-scan-lookup. Each raises sr-* events (sr-open, sr-created, sr-payment, sr-completed, …, listed in the types) and sr-sign-in-required. The kit never draws a map: the locker map uses the app's own sMap map through the adapter. In React:

import { PickupList } from '@superrouteca/sapp-react/ui';
<SuperrouteProvider client={sr}><PickupList onShowCode={showQr} /></SuperrouteProvider>

Native UI kits

The same components on SwiftUI, Jetpack Compose, React Native, WPF and WinForms, following the UI kit reference exactly as the Flutter and web kits do, with the same strings in 14 languages (generated from ui/strings/ by tools/ui_strings.py). Each component keeps its state and calls in a testable model (an ObservableObject on SwiftUI, a state holder on Compose, a view model in Superroute.SApp.UI shared by WPF and WinForms) and draws it with the platform's own controls. The app supplies what the kit leaves to it: images on Compose, the in-app browser for sign-in, files, photos, the position, the scanner, card payments and the sMap map through its adapter.

import SAppUI
SuperrouteScope(client: client, theme: ThemeTokens(branding: branding)) {
    PickupList(onShowCode: { code in showQR(code) })
}
SuperrouteScope(client = client, theme = ThemeTokens(branding)) {
    PickupList(onShowCode = { code -> showQr(code) })
}
import { PickupList } from '@superrouteca/sapp-react-native/ui';
<SuperrouteProvider client={sr}><PickupList onShowCode={(code) => showQr(code)} /></SuperrouteProvider>

Example apps, built against the published packages: SwiftUI, Android, React Native, WPF and WinForms.

Errors

Every failed call throws one error type (SuperrouteError / SuperrouteException) with the HTTP status, the server's code (invalid_token, insufficient_scope, access_denied, APP_ACCESS_DENIED, …) and the parsed body. signInRequired is true when the person must sign in again. Repeatable calls are retried on a lost connection and on temporary server errors; writes are never retried.

How the clients are made

The API methods are generated from the specs Superroute publishes (specs/): one method per operation. Never edit */generated/*; run tools/sync-specs.sh /path/to/Superroute to copy newer specs and regenerate. CI fails when the generated files do not match the specs.

Tests

npm install && npm test        # js, react, react-native
cd dart && dart pub get && dart test
swift test
cd kotlin && ./gradlew :sapp:test
dotnet test dotnet/tests/Superroute.SApp.Tests/Superroute.SApp.Tests.csproj
pip install -e python && pytest python/tests
cd php && composer install && vendor/bin/phpunit
cd go && go test ./...
cd java && ./gradlew test
cd flutter_ui && flutter test

Metadata

Release files for superroute-sapp 0.2.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for superroute-sapp 0.2.5
File Size Uploaded Provenance Actions
superroute_sapp-0.2.5.tar.gz 58.3 kB With provenance Details for superroute_sapp-0.2.5.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for superroute-sapp 0.2.5
File Interpreter ABI Platform Actions
superroute_sapp-0.2.5-py3-none-any.whl Python 3 none any Details for superroute_sapp-0.2.5-py3-none-any.whl

Total release size: 104.0 kB

Release files / superroute_sapp-0.2.5.tar.gz

Download URL superroute_sapp-0.2.5.tar.gz
Size 58.3 kB
Tags Source
SHA-256 checksum
How to use checksums
888f23cb44d0b29f8e645b6f854d9588e002e595c24238989a2129feaa83e4a4
BLAKE2b-256 checksum
How to use checksums
12df52a2c3909d8c549e9ba0c04a9783ce47095dd78174a18ec4a269f50269cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / superroute_sapp-0.2.5-py3-none-any.whl

Download URL superroute_sapp-0.2.5-py3-none-any.whl
Size 45.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d5c332ef8848504b2287a8aa7780c227c7f5ec4b375443c4a5d2b3bd2a79ac9b
BLAKE2b-256 checksum
How to use checksums
00dd8e50ce5fd7c39fb800ac60bf374f7e97b40b1dba0d03557fcde2890470ac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Security for superroute-sapp 0.2.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for superroute_sapp-0.2.5.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page