@supertokens-plugins/squadup-nodejs
v0.3.2
Published
SquadUp ticketing plugin for SuperTokens
Readme
SuperTokens SquadUp Plugin
Adds an authenticated endpoint for listing SquadUp tickets for the current SuperTokens user.
import SquadUpPlugin from "@supertokens-plugins/squadup-nodejs";
SuperTokens.init({
experimental: {
plugins: [
SquadUpPlugin.init({
apiKey: process.env.SQUADUP_API_KEY!,
}),
],
},
});Endpoint
GET /auth/plugin/squadup/tickets
The endpoint requires a SuperTokens session. It uses a passwordless email login method, or a verified third-party email login method, as the SquadUp lookup email. The method's tenantIds must include the verified session tenant. Linked-user login methods from other tenants are excluded; no eligible method returns HTTP 400 without calling SquadUp.
Tenant credentials
Configure exactly one of apiKey: string or
resolveApiKey: ({ tenantId, userContext }) => Promise<string | undefined>.
Static credentials remain supported. The resolver runs on every valid request;
credentials are never cached or written into shared plugin configuration.
tenantId comes exclusively from the verified session's getTenantId(), never
from query parameters. userContext is the SDK request context.
SquadUpPlugin.init({
resolveApiKey: async ({ tenantId, userContext }) => {
return tenantCredentials.getSquadUpApiKey(tenantId, userContext);
},
});Return undefined when the tenant has no integration configured: the endpoint
returns HTTP 503. Resolver exceptions or invalid resolved keys return a sanitized
HTTP 500. Neither case calls SquadUp or looks up the user. Keys must be non-empty
strings. The plugin's own logs omit exception details, upstream URLs and tokens.
SquadUp requests carry the API key in the URL's access_token query parameter.
HTTP tracing and other request instrumentation can record that URL independently
of plugin logging. Consumers must omit these credential-bearing requests from
tracing or redact their credentials before recording/exporting them.
Pagination and email cache
?pageSize=25 controls the upstream page_size. defaultPageSize and
maxPageSize both default to 100 and must be positive integers, with
defaultPageSize <= maxPageSize. Invalid or oversized queries return HTTP 400
before credential resolution, user lookup or SquadUp calls.
Each plugin instance has a bounded, least-recently-used email cache keyed by
[tenantId, userId]. Defaults: emailCache: { ttlMs: 30_000, maxEntries: 1000 }.
Set emailCache: false, ttlMs: 0, or maxEntries: 0 to disable it.
Concurrent misses for a resident entry share one user lookup; thrown failures are
not cached. TTL starts when the lookup completes. Eviction can remove pending
lookups too, so a subsequent request may start a new lookup under capacity pressure.
Email, verification, and login-method tenant membership changes can remain stale for up to the cache TTL (30 seconds by default). Unsupported/missing emails are cached for the same interval. Disable caching when every request must recheck current user state. Tickets and tenant credentials are never cached.
Per-ticket QR/PDF visibility
ticketAvailabilityWindowMs defaults to the numeric value 7_200_000 (two hours).
It accepts either a finite, non-negative number or a synchronous callback:
SquadUpPlugin.init({
apiKey: process.env.SQUADUP_API_KEY!,
ticketAvailabilityWindowMs: ({ event, ticket, tenantId, userContext }) => {
return ticket.type === "VIP" ? 24 * 60 * 60 * 1000 : 2 * 60 * 60 * 1000;
},
});The callback receives the upstream event and ticket as SDK JSONObject values,
plus the verified tenant and request context. It runs once per ticket and must
return a finite, non-negative number; exceptions and invalid results produce a
sanitized HTTP 500.
Visibility affects only each ticket's qrcode_str and pdf_url. Events,
tickets and their other metadata remain in the response, including mixed events
with both available and unavailable tickets. QR/PDF are visible when
eventStart - now <= window, including the exact boundary and past events.
The ticket's event.start_at takes precedence; if absent/null, the enclosing
event's start_at is used. Unknown or invalid dates hide QR/PDF (null);
an explicitly invalid ticket date does not fall back to the enclosing event.
Supported timestamps use YYYY-MM-DDTHH:mm:ss[.fraction]Z or an explicit
±HH:mm offset, with valid calendar and clock components. Timezone-free dates,
normalized impossible dates, and the unknown offset -00:00 are rejected.
Fractional seconds are supported beyond millisecond precision; visibility rounds
up to the next millisecond when necessary so truncation cannot reveal tickets early.
Debugging failures
Packing/publishing runs prepack to rebuild the package and smoke-test both compiled
entrypoints, including the embedded version, numeric IDs, missing metadata, and upstream
404 handling. Run npm run build && npm run test:package to verify artifacts locally.
For a local test with a real token from SSM, build the plugin and run from this package:
npm run build
node scripts/diagnose.cjs --tenant <tenantId> --email <email> --profile st-adminPass --token '<token>' to supply a token directly and skip AWS entirely. This does
not update SSM. A literal token can appear in shell history and process arguments.
The runner reads /managed-backend-api/production/tenants/<tenantId>/squadup-token
from SSM in us-east-2 (override with --environment and --region). It needs the
AWS CLI and an authenticated profile with SSM/KMS read access. It uses a local
session/email fixture and makes a real SquadUp request through the built plugin;
it does not test SuperTokens authentication or the ECS task role. Normal output includes
HTTP status, event count, and safe plugin failure diagnostics. The local runner prints
raw setup errors and their causes for troubleshooting; redact sensitive values before
sharing those errors. Successful response bodies and supplied tokens are not printed.
Set enableDebugLogs: true to write failure diagnostics to stderr. Each failure includes
tenantId, stage, and durationMs, plus upstreamStatus when a response was received.
Validation failures include the first failing schema field, expectedType, and
actualType; network failures include an allowlisted transportCode when available.
Stages distinguish credential resolution, email lookup, upstream transport/HTTP, JSON
parsing, response validation, and visibility-policy failures. No request/response
bodies, email addresses, URLs, QR codes, raw exception messages, or user context are logged.
HTTP 404 remains a successful empty list and is not logged as a failure.
Example: {"stage":"response_validation","tenantId":"tenant-a","durationMs":150,"upstreamStatus":200,"field":"attendees[0].attendee_guests","expectedType":"array","actualType":"object"}.
These are stderr debug logs, not OpenTelemetry spans. Consumers must collect stderr to see them; the existing public API error responses remain sanitized.
Responses
Event and ticket metadata passes through as returned by SquadUp: no required IDs,
names, types, images, or locations, and no field-type enforcement or ID conversion.
Unknown fields and null values are preserved. The plugin only checks the objects
and arrays it traverses: attendees, each attendee's event and attendee_guests,
and each guest's ticket. It assembles event tickets arrays and applies the
configured QR/PDF visibility policy; unknown dates hide QR/PDF without rejecting metadata.
| HTTP | Meaning |
| ---- | ------------------------------------------------------------------------------- |
| 200 | { status: "OK", events: [...] } (an empty attendees list yields events: []) |
| 400 | Invalid page size or no supported verified email (BAD_INPUT_ERROR) |
| 401 | Missing session |
| 500 | Credential resolver, user lookup, or visibility-policy failure |
| 502 | SquadUp transport, non-2xx except 404, invalid JSON, or invalid response fields |
| 503 | Tenant integration not configured |
SquadUp HTTP 404 means no tickets exist for the email and returns HTTP 200 with
{ status: "OK", events: [] }, regardless of the upstream response body.
Error responses contain status and a sanitized message. Successful upstream responses
must contain an attendees array with event and guest/ticket objects. Fields used
for traversal are checked before mapping; metadata fields are not validated.
