SDK UX Contract v1
Status: product contract. This document defines the default developer-facing SDK UX. It does not claim that every server, package, provider, and public endpoint is already aligned. Implementation and E2E status stays in each service/SDK repository and work log.
Goal
The default SDK experience should start from what the app developer wants to do, not from Spectra's server topology.
- Basic examples expose product concepts: project, auth, recipient, sender, subject, body, file, visibility, room, message.
- Basic examples expose server authority as
Project ID + Secret Key, with service availability controlled by Project dashboard service toggles. - Basic examples do not expose internal routing concepts such as Delivery endpoint URLs, service token audience, provider adapter details, worker queues, SMTP mode, storage object-store details, or email template IDs.
- Endpoint overrides, service-token diagnostics, template-based rendering, provider credentials, legacy presets, and queue/provider tuning are advanced operations.
- Advanced operations can exist, but they must not be required for the first successful send, upload, or chat message.
Project ID, Secret Key, and Service Toggles
The primary developer-facing key model is Project ID + Secret Key.
Secret Key is a secret value. Console/API shows the raw value only once, immediately after first issue or regeneration. After that, the raw value cannot be viewed again, so the user must store it. A Project has exactly one active Secret Key.
The Project Secret Key is bound to one environment: test or live. Before first issue, the user can choose the environment. On regeneration, the user can choose the environment for the new Secret Key. Regeneration activates the new Secret Key and the previous Secret Key must no longer be usable.
The Project Secret Key itself has no expiration date. Access is controlled by key regeneration, revocation, or disabling the relevant Project service toggle. User-facing Secret Key docs and UI should not expose an expiration-date input or display. Do not document expires_at or similar expiry fields as part of the user-facing Project Secret Key contract.
Secret Key does not store feature permissions. Email, Push, Storage, Chat, and Call availability is controlled by Project dashboard service toggles. A server request proves the Project and Secret Key environment with projectId + secretKey; the producer then checks whether the requested service is enabled. If the service is disabled, the producer returns a service-disabled error even when the Secret Key is valid.
Public-client configuration is not a primary user-managed key in the new product contract. Current SDK/API fields may still expose publicClientId until implementation alignment is complete; document it only as current package compatibility, not as a product key users need to understand.
Legacy or advanced docs may mention older key/token/preset names for compatibility, migration, or diagnostics. Expiry fields on app-user tokens, verification codes, service tokens, socket tickets, or call participant tokens remain valid in those separate token contracts; they are not Project Secret Key expiry.
Basic App Setup
Default browser and mobile SDK examples use public configuration only.
const auth = new SpectraAuthClient({
projectId,
// publicClientId may remain in current packages as a compatibility field
// until the SDK/API implementation aligns with the Project ID + Secret Key contract.
publicClientId,
environment,
redirectUri,
});
const storage = new SpectraStorageClient({ projectId, auth });
const chat = new SpectraChatClient({ projectId, auth });
Default SDK docs should not require values like deliveryBaseURL, service token audience, SMTP credentials, APNs/FCM server credentials, object-store credentials, or a Secret Key for the first browser/mobile setup. They should state that the Secret Key is environment-bound, secret, has no expiration date, and is shown only once after issue/regeneration.
Basic Server Setup
Trusted backends can read the Secret Key from a server secret store or environment variable. Whether a consuming project exposes the Secret Key to browser/mobile code is an app architecture decision; this contract only requires that docs describe it as an environment-bound no-expiry secret value that is shown once.
const projectId = process.env.SPECTRA_PROJECT_ID;
const secretKey = process.env.SPECTRA_SECRET_KEY;
const environment = process.env.SPECTRA_ENVIRONMENT ?? "test";
Service toggles decide which server operations are allowed. For example, the same valid Secret Key can prove the Project for Email and Storage requests, but Email requests are accepted only when Email is enabled and Storage requests are accepted only when Storage is enabled. Producers may still validate internal scopes/audiences for app-user tokens or service internals, but the user-facing Secret Key does not carry feature flags or expiry.
Email default UX is simple direct sending from a server-side SDK or trusted backend. A developer should be able to send a plain message without creating a template first.
await email.send({
to: "user@example.com",
from: "no-reply@spectra.kr",
subject: "Welcome to Spectra",
text: "Your workspace is ready.",
});
The default server-side contract is:
send({
to,
from?,
subject,
text?,
html?,
contentType?,
})
Rules:
- The call is made from a trusted backend with
Project ID + Secret Key, and the Project has the Email service enabled. subjectis required.- At least one of
textorhtmlis required. contentTypeis optional and inferred fromtext/htmlin the common case.fromcan default to the verified project sender when omitted.templateId,templateVersion, template data, tracking policy, provider selection, queue priority, and batch sending are advanced options.- Basic docs should not present
templateIdas the first or required way to send email.
Email Verification
Verification email UX also defaults to direct body input. The SDK or server can inject the generated code into the direct body, but developers should not need to create a template for the first verification flow.
const challenge = await email.verifications.send({
to: "user@example.com",
subject: "Your sign-in code",
text: "Your code is {{code}}.",
expiresInSeconds: 600,
});
await email.verifications.verify({
verificationId: challenge.verificationId,
code,
});
If a browser/mobile Notification SDK exposes email verification, its basic docs should describe direct fields such as email or to, subject, text, html, and expiresInSeconds. templateId remains an advanced optional field for teams that already operate managed templates.
Storage
Storage default UX should make file visibility explicit and readable.
await storage.uploadFile({
file,
path: "/avatars/me.png",
visibility: "private",
});
await storage.uploadFile({
file,
path: "/public/profile-card.png",
visibility: "public_read",
});
The basic visibility contract is:
visibility: "private" | "public_read"
Rules:
- Default visibility is
private. privatemeans the current app-user or authorized server flow needs a signed or authorized read path.public_readmeans anyone with the public read URL can read the object. Write, overwrite, delete, lifecycle, quota, audit, and moderation remain governed by Storage policy.- Browser/mobile Storage SDK uses app-user authorization through Auth. Server-side Storage management uses
Project ID + Secret Key, and the Project must have Storage enabled. - Bucket policy, object-store ACLs, CDN configuration, signed PUT metadata headers, and lifecycle tuning are implementation details or advanced settings.
Chat Files
Chat file sending should be a Chat SDK experience. The app should not have to manually compose Storage SDK upload, Storage object reference mapping, and Chat message payload for the default flow.
Target default UX:
await chat.sendMessageWithFiles(roomId, {
text: "사진 첨부",
files: [
{
file,
visibility: "private",
},
],
});
Rules:
- Chat SDK may internally use Storage SDK or Storage Platform APIs to upload bytes.
- Chat message storage still keeps Storage object references, not binary bytes.
- Apps may still use advanced manual composition when they need custom upload policy, reuse of an existing object, or non-chat attachment workflows.
- Basic Chat docs should present
sendMessageWithFiles()or an equivalent convenience API as the default file-send direction once implementation aligns.
Advanced Surface
Advanced docs may expose the following, but first-run docs should not depend on them:
- legacy key/token/preset names
deliveryBaseURL,storageBaseURL,chatBaseURL, or generic endpoint override fields- service token audience, service-token exchange internals, JWT
auddebugging templateId, template versions, template data, provider routing, queue priority- object-store buckets, S3/MinIO ACLs, signed URL internals, metadata header encoding
- APNs/FCM/Gmail provider credentials and SMTP mode
When an implementation currently requires one of these values for a basic use case, treat it as SDK/service alignment work rather than a consumer responsibility.
Documentation Acceptance
Default developer docs should pass these checks:
- Email first example uses
send({ to, from?, subject, text?, html?, contentType? }), nottemplateId. - Server authority is described as
Project ID + Secret Key, and service availability is controlled by service toggles. - Secret Key handling says the raw value is visible only once after issue/regeneration, has no expiration date, is bound to
testorlive, and there is one active Secret Key per Project. - Secret Key issue/regeneration docs say the user can choose the environment, and regeneration makes the previous Secret Key unusable.
- Public-client configuration is not presented as a primary user-managed key; any
publicClientIdmention is clearly marked as current SDK/API compatibility. - Verification first example uses a direct body, not a template-only call.
- Setup examples do not include
deliveryBaseURLor service token audience. - Default browser/mobile examples do not require Secret Keys.
- Storage upload examples show
visibility: "private"orvisibility: "public_read"when visibility matters. - Chat file examples prefer
sendMessageWithFiles()or an equivalent Chat SDK wrapper once the SDK implementation exists. - Any mismatch between this contract and current packages is clearly marked as pending implementation alignment.