Documentation
¶
Overview ¶
Package spectrum is a Go client for the Photon Spectrum API (https://photon.codes/docs/api-reference/introduction) — the HTTP management plane for a Spectrum project's webhooks, platforms, lines, and users.
The API uses HTTP Basic auth: the username is your projectId and the password is your projectSecret. Credentials are scoped to a single project, so a Client is too:
client := spectrum.New(projectID, projectSecret)
project, err := client.Projects.Get(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println(project.Name)
Functionality is grouped into services mirroring the API's resource groups: Projects, Billing, Lines, Platforms, Users, Webhooks, Voice, IMessage, WhatsApp, Slack, and Fusor.
Errors ¶
Every non-2xx response is returned as an *Error carrying the HTTP status code and the server's message:
_, err := client.Users.Get(ctx, userID)
var apiErr *spectrum.Error
if errors.As(err, &apiErr) && apiErr.NotFound() {
// handle 404
}
Retries ¶
Requests that fail with 408, 429, or a 5xx status — as well as transport-level errors — are retried with exponential backoff and jitter. By default only idempotent methods (GET, PUT, DELETE) are retried; see WithMaxRetries and WithRetryAllMethods.
Receiving webhooks ¶
The webhook subpackage verifies X-Spectrum-Signature headers and decodes event payloads. See package github.com/datacatcorp/gospectrum/webhook.
Sending messages and downloading attachments ¶
The runtime (gRPC) subpackages send messages, transfer attachments and media, and subscribe to live events, bootstrapped from this package's credentials via their ConnectCloud functions. See packages github.com/datacatcorp/gospectrum/imessage, github.com/datacatcorp/gospectrum/whatsapp, and github.com/datacatcorp/gospectrum/slack.
Dashboard API ¶
The dashboard subpackage covers the Photon Dashboard API (project CRUD and the RFC 8628 device-login flow used by the CLI). See package github.com/datacatcorp/gospectrum/dashboard.
Index ¶
- Constants
- type AddedLine
- type AvatarUpload
- type BillingService
- type BillingStatus
- type BillingSyncStatus
- type Client
- type CreateTemplateRequest
- type CreateUserRequest
- type CreatedTemplate
- type EditTemplateRequest
- type Error
- type FusorService
- type IMessageInfo
- type IMessageLine
- type IMessagePlatform
- type IMessageService
- type IMessageServiceType
- type IMessageTokens
- type Line
- type LineAvatarUpload
- type LineBilling
- type LineProfile
- type LineProfileSummary
- type LineStatus
- type LinesService
- func (s *LinesService) AddIMessage(ctx context.Context) (*AddedLine, error)
- func (s *LinesService) CommitAvatar(ctx context.Context, lineID, key string) (*LineProfile, error)
- func (s *LinesService) CreateAvatarUpload(ctx context.Context, lineID, contentType string) (*LineAvatarUpload, error)
- func (s *LinesService) Delete(ctx context.Context, lineID string) (*LineBilling, error)
- func (s *LinesService) GetProfile(ctx context.Context, lineID string) (*LineProfile, error)
- func (s *LinesService) List(ctx context.Context, opts *ListLinesOptions) ([]Line, error)
- func (s *LinesService) Route(ctx context.Context) (*RoutedLine, error)
- func (s *LinesService) UpdateProfile(ctx context.Context, lineID string, req UpdateLineProfileRequest) (*LineProfile, error)
- func (s *LinesService) UploadAvatar(ctx context.Context, lineID, contentType string, image io.Reader) (*LineProfile, error)
- type ListLinesOptions
- type ListTemplatesOptions
- type ListUsersOptions
- type Nullable
- type Option
- type Platform
- type Platforms
- type PlatformsService
- func (s *PlatformsService) Get(ctx context.Context) (*Platforms, error)
- func (s *PlatformsService) SetIMessageAutoScale(ctx context.Context, autoScale bool) (*Platforms, error)
- func (s *PlatformsService) SetVoiceIMessageEnabled(ctx context.Context, enabled bool) (*Platforms, error)
- func (s *PlatformsService) Toggle(ctx context.Context, platform Platform, enabled bool) (*Platforms, error)
- func (s *PlatformsService) UpdateMetadata(ctx context.Context, platform Platform, metadata any) (*Platforms, error)
- type ProfileSyncError
- type ProfileSyncResult
- type ProfileSyncState
- type ProfileSyncStatus
- type Project
- type ProjectProfile
- type ProjectProfileSummary
- type ProjectsService
- func (s *ProjectsService) CommitAvatar(ctx context.Context, key string) (string, error)
- func (s *ProjectsService) CreateAvatarUpload(ctx context.Context, contentType string) (*AvatarUpload, error)
- func (s *ProjectsService) Get(ctx context.Context) (*Project, error)
- func (s *ProjectsService) GetProfile(ctx context.Context) (*ProjectProfile, error)
- func (s *ProjectsService) GetProfileSyncStatus(ctx context.Context) (*ProfileSyncStatus, error)
- func (s *ProjectsService) SyncProfile(ctx context.Context) (*ProfileSyncResult, error)
- func (s *ProjectsService) UpdateProfile(ctx context.Context, req UpdateProfileRequest) (*ProjectProfile, error)
- func (s *ProjectsService) UpdateSlug(ctx context.Context, slug string) (*Slug, error)
- func (s *ProjectsService) UploadAvatar(ctx context.Context, contentType string, image io.Reader) (string, error)
- type RegisteredWebhook
- type RoutedLine
- type SIPInboundConfig
- type SimplePlatform
- type SlackAppConfig
- type SlackInstallation
- type SlackService
- func (s *SlackService) DeleteAppConfig(ctx context.Context) error
- func (s *SlackService) DeleteInstallation(ctx context.Context, teamID string) error
- func (s *SlackService) GetAppConfig(ctx context.Context) (*SlackAppConfig, error)
- func (s *SlackService) IssueTokens(ctx context.Context) (*SlackTokens, error)
- func (s *SlackService) ListInstallations(ctx context.Context) ([]SlackInstallation, error)
- func (s *SlackService) Setup(ctx context.Context, req SlackSetupRequest) (*SlackSetupResult, error)
- func (s *SlackService) UpsertAppConfig(ctx context.Context, req UpsertSlackAppConfigRequest) (*SlackAppConfig, error)
- func (s *SlackService) UpsertInstallation(ctx context.Context, teamID string, req UpsertSlackInstallationRequest) (*SlackInstallation, error)
- type SlackSetupRequest
- type SlackSetupResult
- type SlackTeam
- type SlackTokens
- type Slug
- type Subscription
- type SubscriptionStatus
- type Template
- type TemplateCategory
- type TemplateComponent
- type TemplateList
- type TemplatePaging
- type TemplateParameterFormat
- type TemplateQualityScore
- type TemplateStatus
- type Token
- type UpdateLineProfileRequest
- type UpdateProfileRequest
- type UpsertSIPInboundRequest
- type UpsertSlackAppConfigRequest
- type UpsertSlackInstallationRequest
- type User
- type UserList
- type UserType
- type UsersService
- func (s *UsersService) Create(ctx context.Context, req CreateUserRequest) (*User, error)
- func (s *UsersService) Delete(ctx context.Context, userID string) error
- func (s *UsersService) Get(ctx context.Context, userID string) (*User, error)
- func (s *UsersService) List(ctx context.Context, opts *ListUsersOptions) (*UserList, error)
- func (s *UsersService) RedirectURL(userID, msg string) string
- func (s *UsersService) ResolveRedirect(ctx context.Context, userID, msg string) (string, error)
- type VoicePlatform
- type VoiceService
- func (s *VoiceService) DeleteSIPInbound(ctx context.Context) error
- func (s *VoiceService) GetSIPInbound(ctx context.Context) (*SIPInboundConfig, error)
- func (s *VoiceService) IssueToken(ctx context.Context) (*Token, error)
- func (s *VoiceService) UpsertSIPInbound(ctx context.Context, req UpsertSIPInboundRequest) (*SIPInboundConfig, error)
- type Webhook
- type WebhooksService
- type WhatsAppAccount
- type WhatsAppLine
- type WhatsAppLineState
- type WhatsAppRegistrationState
- type WhatsAppService
- func (s *WhatsAppService) CreateTemplate(ctx context.Context, accountID string, req CreateTemplateRequest) (*CreatedTemplate, error)
- func (s *WhatsAppService) DeleteTemplate(ctx context.Context, accountID, templateID, name string) error
- func (s *WhatsAppService) EditTemplate(ctx context.Context, accountID, templateID string, req EditTemplateRequest) error
- func (s *WhatsAppService) IssueTokens(ctx context.Context) (*WhatsAppTokens, error)
- func (s *WhatsAppService) ListAccounts(ctx context.Context) ([]WhatsAppAccount, error)
- func (s *WhatsAppService) ListTemplates(ctx context.Context, accountID string, opts ListTemplatesOptions) (*TemplateList, error)
- type WhatsAppTokens
Constants ¶
const DefaultBaseURL = "https://spectrum.photon.codes"
DefaultBaseURL is the production Spectrum API host. HTTPS only — the API rejects plaintext connections.
const Version = "0.1.0"
Version is the client library version, reported in the User-Agent.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AddedLine ¶
type AddedLine struct {
Line IMessageLine `json:"line"`
Billing LineBilling `json:"billing"`
}
AddedLine is the result of AddIMessage.
type AvatarUpload ¶
type AvatarUpload struct {
// UploadURL accepts a single HTTP PUT of the image bytes.
UploadURL string `json:"uploadUrl"`
// Key is passed to the corresponding commit call after uploading.
Key string `json:"key"`
}
AvatarUpload is a presigned upload slot for an avatar image.
type BillingService ¶
type BillingService service
BillingService reads the project's plan and async-billing sync state.
func (*BillingService) GetStatus ¶
func (s *BillingService) GetStatus(ctx context.Context) (*BillingStatus, error)
GetStatus returns the project's async-billing sync state.
GET /projects/{projectId}/billing/status
func (*BillingService) GetSubscription ¶
func (s *BillingService) GetSubscription(ctx context.Context) (*Subscription, error)
GetSubscription returns the current plan tier and subscription status.
GET /projects/{projectId}/billing/subscription
type BillingStatus ¶
type BillingStatus struct {
SyncStatus BillingSyncStatus `json:"syncStatus"`
LastSyncedAt *string `json:"lastSyncedAt"`
Error *string `json:"error"`
Quantity float64 `json:"quantity"`
LastProrationAmount *float64 `json:"lastProrationAmount"`
}
BillingStatus is the project's async-billing sync state. After adding or deleting a line, poll GetStatus until SyncStatus is no longer BillingSyncing to read the final proration.
type BillingSyncStatus ¶
type BillingSyncStatus string
BillingSyncStatus is the state of the async billing sync that runs after line changes.
const ( BillingInSync BillingSyncStatus = "in_sync" BillingSyncing BillingSyncStatus = "syncing" BillingFailed BillingSyncStatus = "failed" )
type Client ¶
type Client struct {
// Services mirroring the API's resource groups.
Projects *ProjectsService
Billing *BillingService
Lines *LinesService
Platforms *PlatformsService
Users *UsersService
Webhooks *WebhooksService
Voice *VoiceService
IMessage *IMessageService
WhatsApp *WhatsAppService
Slack *SlackService
Fusor *FusorService
// contains filtered or unexported fields
}
Client is a Spectrum API client scoped to a single project. Create one with New. Its zero value is not usable.
A Client is safe for concurrent use by multiple goroutines.
func New ¶
New returns a Client authenticated as the given project.
Retrieve credentials with `photon projects show` or from the dashboard at https://app.photon.codes.
func (*Client) Do ¶
func (c *Client) Do(ctx context.Context, method, path string, query url.Values, body, out any) error
Do performs an authenticated request against the API and decodes the envelope's `data` field into out (unless out is nil). path must start with "/" and query may be nil. A non-nil body is sent as JSON.
The typed services cover every documented endpoint; Do is the escape hatch for endpoints added to the API before they are added here.
type CreateTemplateRequest ¶
type CreateTemplateRequest struct {
Name string `json:"name"`
Language string `json:"language"`
Category TemplateCategory `json:"category"`
Components []TemplateComponent `json:"components"`
ParameterFormat TemplateParameterFormat `json:"parameterFormat,omitempty"`
AllowCategoryChange bool `json:"allowCategoryChange,omitempty"`
}
CreateTemplateRequest maps to Meta's POST /{waba_id}/message_templates payload. Components follow Meta's components reference and are forwarded unchanged.
type CreateUserRequest ¶
type CreateUserRequest struct {
Type UserType `json:"type"`
// PhoneNumber is the user's own number in E.164 form.
PhoneNumber string `json:"phoneNumber"`
// AssignedPhoneNumber is required when Type is UserDedicated and
// must be omitted for UserShared.
AssignedPhoneNumber string `json:"assignedPhoneNumber,omitempty"`
FirstName *string `json:"firstName,omitempty"`
LastName *string `json:"lastName,omitempty"`
Email *string `json:"email,omitempty"`
}
CreateUserRequest creates a user. Type is required.
For UserShared, the server assigns a phone number from the shared pool and enforces the project's maxSharedUsers. Re-creating with an existing active PhoneNumber returns that same user and updates its name/email from any values supplied (nil fields are left unchanged).
For UserDedicated, AssignedPhoneNumber must be one of the project's dedicated line numbers (pick one with Lines.Route).
type CreatedTemplate ¶
type CreatedTemplate struct {
ID string `json:"id"`
Status TemplateStatus `json:"status"`
Category TemplateCategory `json:"category"`
}
CreatedTemplate is Meta's acknowledgement of a new template. Newly created templates start in TemplatePending and require Meta approval before they can be sent.
type EditTemplateRequest ¶
type EditTemplateRequest struct {
Components []TemplateComponent `json:"components,omitempty"`
Category TemplateCategory `json:"category,omitempty"`
MessageSendTTLSeconds int `json:"messageSendTtlSeconds,omitempty"`
}
EditTemplateRequest edits an existing template. Only Components, Category, and MessageSendTTLSeconds are editable — Meta forbids changing name and language. At least one field must be set (an empty body returns 400). Editing is subject to Meta's template lifecycle: APPROVED templates can edit components/category (category changes require re-approval), REJECTED templates can edit any field, PAUSED templates have limited edits.
type Error ¶
type Error struct {
// StatusCode is the HTTP status code of the response.
StatusCode int
// Message is the server's explanation, when the body carried one.
Message string
// Method and Path identify the request that failed.
Method string
Path string
// Body is the raw response body, for debugging.
Body []byte
}
Error is the error type returned for any non-2xx API response.
The Spectrum API's documented status codes:
401 missing or invalid project credentials 404 resource not found or already deleted 409 conflict — e.g. a resource with the same key already exists 422 request body failed schema validation 429 rate limit exceeded (default 5 requests/second/project) 5xx Spectrum-side error, safe to retry with backoff
func (*Error) Conflict ¶
Conflict reports whether the response was 409 — for example, a resource with the same key already exists.
func (*Error) NotFound ¶
NotFound reports whether the response was 404 — resource not found or already deleted.
func (*Error) RateLimited ¶
RateLimited reports whether the response was 429 — the project exceeded its request rate limit.
func (*Error) ServerError ¶
ServerError reports whether the response was a 5xx — a Spectrum-side failure that is safe to retry with backoff.
func (*Error) Unauthorized ¶
Unauthorized reports whether the response was 401 — missing or invalid project credentials.
func (*Error) Validation ¶
Validation reports whether the response was 422 — the request body failed schema validation.
type FusorService ¶
type FusorService service
FusorService issues tokens for the Photon Fusor service.
func (*FusorService) IssueToken ¶
func (s *FusorService) IssueToken(ctx context.Context) (*Token, error)
IssueToken issues a short-lived LightAuth JWT bound to the Photon Fusor service (codes.photon.spectrum.fusor) and the requesting project. The token's subject is the project id; downstream Fusor services treat it as the project-scoped capability.
POST /projects/{projectId}/fusor/token
type IMessageInfo ¶
type IMessageInfo struct {
Type IMessageServiceType `json:"type"`
}
IMessageInfo is the project's iMessage provisioning summary.
type IMessageLine ¶
type IMessageLine struct {
ID string `json:"id"`
PhoneNumber string `json:"phoneNumber"`
Profile LineProfileSummary `json:"profile"`
Status LineStatus `json:"status"`
CreatedAt string `json:"createdAt"`
}
IMessageLine is a dedicated iMessage phone line.
type IMessagePlatform ¶
type IMessagePlatform struct {
Enabled bool `json:"enabled"`
// AutoScale allocates additional dedicated lines automatically as
// user counts grow.
AutoScale bool `json:"autoScale"`
}
IMessagePlatform is the iMessage platform entry.
type IMessageService ¶
type IMessageService service
IMessageService reads the project's iMessage provisioning and issues runtime tokens.
func (*IMessageService) Info ¶
func (s *IMessageService) Info(ctx context.Context) (*IMessageInfo, error)
Info returns whether the project's iMessage service is shared or dedicated.
GET /projects/{projectId}/imessage/
func (*IMessageService) IssueTokens ¶
func (s *IMessageService) IssueTokens(ctx context.Context) (*IMessageTokens, error)
IssueTokens issues iMessage LightAuth tokens for the project.
POST /projects/{projectId}/imessage/tokens
func (*IMessageService) SharedAvailability ¶
SharedAvailability checks whether a new shared iMessage number can be assigned to the given phone number (E.164) under this project. It mirrors the allocation rules used by Users.Create, including reuse of a soft-deleted user's previously assigned number within the same project.
GET /projects/{projectId}/imessage/shared/availability
type IMessageServiceType ¶
type IMessageServiceType string
IMessageServiceType is how the project's iMessage capacity is provisioned.
const ( IMessageShared IMessageServiceType = "shared" // IMessageDedicated uses lines dedicated to the project // (Business plan). IMessageDedicated IMessageServiceType = "dedicated" )
type IMessageTokens ¶
type IMessageTokens struct {
Type IMessageServiceType `json:"type"`
// Dedicated projects.
Auth map[string]string `json:"auth,omitempty"`
Numbers map[string]string `json:"numbers,omitempty"`
// Shared projects.
Token string `json:"token,omitempty"`
ExpiresIn int `json:"expiresIn"`
}
IMessageTokens is the result of IssueTokens. Type selects which fields are populated:
- IMessageDedicated: Auth maps instance id → LightAuth token and Numbers maps instance id → phone number.
- IMessageShared: Token is the single LightAuth token.
ExpiresIn is the TTL in seconds shared by every returned token.
type Line ¶
type Line struct {
Platform Platform
IMessage *IMessageLine
WhatsApp *WhatsAppLine
Raw json.RawMessage
}
Line is one entry from List. Exactly one of the platform-specific fields is non-nil, selected by Platform. Raw preserves the original JSON, including fields introduced after this library version.
func (*Line) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler, dispatching on the entry's platform tag.
type LineAvatarUpload ¶
type LineAvatarUpload struct {
ProjectID string `json:"projectId"`
LineID string `json:"lineId"`
UploadURL string `json:"uploadUrl"`
Key string `json:"key"`
}
LineAvatarUpload is a presigned upload slot bound to a line.
type LineBilling ¶
type LineBilling struct {
Quantity *float64 `json:"quantity"`
ProrationAmount *float64 `json:"prorationAmount"`
SyncStatus BillingSyncStatus `json:"syncStatus"`
}
LineBilling is the Stripe adjustment triggered by adding or removing a line. Quantity and ProrationAmount are nil while the async sync is still running — poll Billing.GetStatus until SyncStatus is no longer BillingSyncing.
type LineProfile ¶
type LineProfile struct {
ProjectID string `json:"projectId"`
LineID string `json:"lineId"`
PhoneNumber string `json:"phoneNumber"`
FirstName *string `json:"firstName"`
LastName *string `json:"lastName"`
AvatarURL *string `json:"avatarUrl"`
}
LineProfile is the stored name and avatar for a dedicated iMessage line.
type LineProfileSummary ¶
type LineProfileSummary struct {
FirstName *string `json:"firstName"`
LastName *string `json:"lastName"`
AvatarURL *string `json:"avatarUrl"`
}
LineProfileSummary is the profile embedded in an iMessage line.
type LineStatus ¶
type LineStatus string
LineStatus is the availability of a dedicated iMessage line.
const ( LineAvailable LineStatus = "available" LineUnknown LineStatus = "unknown" )
type LinesService ¶
type LinesService service
LinesService manages the project's dedicated phone lines.
func (*LinesService) AddIMessage ¶
func (s *LinesService) AddIMessage(ctx context.Context) (*AddedLine, error)
AddIMessage allocates a new dedicated iMessage phone number for the project and updates the Stripe subscription quantity with pro-rated billing. Business plan only. (iMessage only for now; WhatsApp Business onboarding flows through the Meta registration endpoints.)
POST /projects/{projectId}/lines/
func (*LinesService) CommitAvatar ¶
func (s *LinesService) CommitAvatar(ctx context.Context, lineID, key string) (*LineProfile, error)
CommitAvatar verifies an uploaded image key bound to this line and updates its stored profile while preserving its name.
POST /projects/{projectId}/lines/{lineId}/profile/avatar/commit
func (*LinesService) CreateAvatarUpload ¶
func (s *LinesService) CreateAvatarUpload(ctx context.Context, lineID, contentType string) (*LineAvatarUpload, error)
CreateAvatarUpload returns a presigned upload URL and a storage key bound to this project and dedicated iMessage line. Most callers can use UploadAvatar instead.
POST /projects/{projectId}/lines/{lineId}/profile/avatar/upload
func (*LinesService) Delete ¶
func (s *LinesService) Delete(ctx context.Context, lineID string) (*LineBilling, error)
Delete deallocates a dedicated line by id. For iMessage lines this decrements the Stripe subscription quantity with pro-rated credit (Business plan only). WhatsApp Business lines are removed without a billing change and return nil billing.
DELETE /projects/{projectId}/lines/{lineId}
func (*LinesService) GetProfile ¶
func (s *LinesService) GetProfile(ctx context.Context, lineID string) (*LineProfile, error)
GetProfile returns the stored name and avatar for a dedicated iMessage line.
GET /projects/{projectId}/lines/{lineId}/profile
func (*LinesService) List ¶
func (s *LinesService) List(ctx context.Context, opts *ListLinesOptions) ([]Line, error)
List returns the dedicated phone lines the project owns across all platforms. On iMessage Free or Pro plans, lines are assigned per-user rather than dedicated to the project — for those, redirect users via Users.RedirectURL instead.
GET /projects/{projectId}/lines/
func (*LinesService) Route ¶
func (s *LinesService) Route(ctx context.Context) (*RoutedLine, error)
Route returns the single best dedicated iMessage line to assign a new user to, load-balancing by active user count and recent growth. Returns a 404 *Error if the project owns no dedicated iMessage lines.
GET /projects/{projectId}/lines/route
func (*LinesService) UpdateProfile ¶
func (s *LinesService) UpdateProfile(ctx context.Context, lineID string, req UpdateLineProfileRequest) (*LineProfile, error)
UpdateProfile merges firstName and/or lastName into this line's stored profile while preserving its other name and avatar.
PATCH /projects/{projectId}/lines/{lineId}/profile
func (*LinesService) UploadAvatar ¶
func (s *LinesService) UploadAvatar(ctx context.Context, lineID, contentType string, image io.Reader) (*LineProfile, error)
UploadAvatar sets a line's avatar in one call: it requests a presigned upload slot, PUTs the image bytes, and commits the key. contentType is the image MIME type, e.g. "image/png".
type ListLinesOptions ¶
type ListLinesOptions struct {
// Platform limits results to a single platform.
Platform Platform
}
ListLinesOptions filters List.
type ListTemplatesOptions ¶
type ListTemplatesOptions struct {
Limit int
Name string
NameOrContent string
Status string
Language string
Category string
// After and Before are opaque Meta paging cursors from a previous
// page's TemplatePaging.
After string
Before string
}
ListTemplatesOptions filters ListTemplates. Limit is required by the API; the other fields are forwarded to Meta Graph as filters.
type ListUsersOptions ¶
type ListUsersOptions struct {
// Type filters to shared or dedicated users.
Type UserType
// IDs batch-fetches specific users by id.
IDs []string
// Search is a partial, case-insensitive match on first/last name,
// phone number, and email.
Search string
// Limit caps the page size (max 500). Zero returns all matches.
Limit int
// Offset skips past earlier matches when paging.
Offset int
}
ListUsersOptions filters and pages List. Pagination is opt-in: leave Limit and Offset zero to return all matches.
type Nullable ¶
type Nullable[T any] struct { // contains filtered or unexported fields }
Nullable distinguishes the three states a PATCH field can be in: omitted from the request (leave unchanged), explicit JSON null (clear the stored value), or a concrete value (set it).
The zero value is "omitted". Struct fields of this type must carry the `omitzero` JSON tag so unset fields stay off the wire:
type req struct {
Username Nullable[string] `json:"username,omitzero"`
}
Build values with NullableOf and Null:
spectrum.NullableOf("alice") // "username":"alice"
spectrum.Null[string]() // "username":null
spectrum.Nullable[string]{} // field omitted
func (Nullable[T]) IsZero ¶
IsZero reports whether the field is unset, which makes `omitzero` drop it during marshaling.
func (Nullable[T]) MarshalJSON ¶
MarshalJSON implements json.Marshaler.
func (*Nullable[T]) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.
type Option ¶
type Option func(*Client)
Option configures a Client.
func WithBaseURL ¶
WithBaseURL overrides the API host, e.g. for a staging backend.
func WithHTTPClient ¶
WithHTTPClient sets the underlying *http.Client. Use it to configure timeouts, proxies, or transport middleware.
func WithMaxRetries ¶
WithMaxRetries sets how many times a failed request is retried on 408/429/5xx responses and transport errors. The default is 2. Pass 0 to disable retries.
func WithRetryAllMethods ¶
func WithRetryAllMethods() Option
WithRetryAllMethods extends retries to POST and PATCH requests. By default only idempotent methods (GET, PUT, DELETE) are retried, since a retried POST that raced a timeout can be applied twice.
func WithUserAgent ¶
WithUserAgent overrides the User-Agent header.
type Platforms ¶
type Platforms struct {
IMessage *IMessagePlatform `json:"imessage"`
WhatsAppBusiness *SimplePlatform `json:"whatsapp_business"`
Voice *VoicePlatform `json:"voice"`
Slack *SimplePlatform `json:"slack"`
}
Platforms is the project's platform configuration, including disabled entries (metadata is preserved across toggles). Entries are nil when the platform has never been configured.
type PlatformsService ¶
type PlatformsService service
PlatformsService toggles platforms on and off for the project and updates platform-specific metadata.
func (*PlatformsService) Get ¶
func (s *PlatformsService) Get(ctx context.Context) (*Platforms, error)
Get returns the project's platform configuration.
GET /projects/{projectId}/platforms/
func (*PlatformsService) SetIMessageAutoScale ¶
func (s *PlatformsService) SetIMessageAutoScale(ctx context.Context, autoScale bool) (*Platforms, error)
SetIMessageAutoScale updates the iMessage platform's autoScale flag.
func (*PlatformsService) SetVoiceIMessageEnabled ¶
func (s *PlatformsService) SetVoiceIMessageEnabled(ctx context.Context, enabled bool) (*Platforms, error)
SetVoiceIMessageEnabled updates the voice platform's imessage_enabled flag.
func (*PlatformsService) Toggle ¶
func (s *PlatformsService) Toggle(ctx context.Context, platform Platform, enabled bool) (*Platforms, error)
Toggle enables or disables a platform for the project. Any previously stored metadata is preserved across toggles.
PATCH /projects/{projectId}/platforms/
func (*PlatformsService) UpdateMetadata ¶
func (s *PlatformsService) UpdateMetadata(ctx context.Context, platform Platform, metadata any) (*Platforms, error)
UpdateMetadata updates platform-specific metadata. It can only be called after the platform is enabled (409 otherwise); use Toggle to change `enabled`. metadata is the platform's metadata object — see the typed helpers SetIMessageAutoScale and SetVoiceIMessageEnabled for the two documented fields.
PATCH /projects/{projectId}/platforms/{platform}
type ProfileSyncError ¶
ProfileSyncError describes a line that failed to sync.
type ProfileSyncResult ¶
type ProfileSyncResult struct {
ProjectID string `json:"projectId"`
// TargetedLineCount is how many dedicated iMessage lines the sync
// targets.
TargetedLineCount int `json:"targetedLineCount"`
}
ProfileSyncResult is the acknowledgement returned by SyncProfile.
type ProfileSyncState ¶
type ProfileSyncState string
ProfileSyncState is the aggregate state of a profile sync.
const ( ProfileSyncInProgress ProfileSyncState = "in_progress" ProfileSyncCompleted ProfileSyncState = "completed" ProfileSyncPartialFailed ProfileSyncState = "partial_failed" ProfileSyncFailed ProfileSyncState = "failed" )
type ProfileSyncStatus ¶
type ProfileSyncStatus struct {
ProjectID string `json:"projectId"`
Status ProfileSyncState `json:"status"`
Total int `json:"total"`
Pending int `json:"pending"`
Synced int `json:"synced"`
Failed int `json:"failed"`
Errors []ProfileSyncError `json:"errors"`
}
ProfileSyncStatus reports per-line convergence of the project profile across dedicated iMessage lines.
type Project ¶
type Project struct {
Name string `json:"name"`
Slug string `json:"slug"`
// Profile is nil when the project has no profile set.
Profile *ProjectProfileSummary `json:"profile"`
}
Project is the project summary returned by Get.
type ProjectProfile ¶
type ProjectProfile struct {
ProjectID string `json:"projectId"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
AvatarURL *string `json:"avatarUrl"`
}
ProjectProfile is the standalone profile resource.
type ProjectProfileSummary ¶
type ProjectProfileSummary struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
AvatarURL *string `json:"avatarUrl"`
// IMessageSynced is true iff the project is Business-tier and every
// active dedicated iMessage line has the current profile applied.
// Free/Pro projects ride a shared line and always report false.
IMessageSynced bool `json:"imessageSynced"`
}
ProjectProfileSummary is the profile embedded in a Project.
type ProjectsService ¶
type ProjectsService service
ProjectsService covers the project itself: display name, slug, profile (name + avatar shown to end users), and profile→line sync.
func (*ProjectsService) CommitAvatar ¶
CommitAvatar validates a previously uploaded image and conditionally propagates the complete project profile. It returns the public avatar URL.
POST /projects/{projectId}/profile/avatar/commit
func (*ProjectsService) CreateAvatarUpload ¶
func (s *ProjectsService) CreateAvatarUpload(ctx context.Context, contentType string) (*AvatarUpload, error)
CreateAvatarUpload returns a presigned PUT URL and the project-bound key to commit after uploading the avatar. Most callers can use UploadAvatar instead, which performs all three steps.
POST /projects/{projectId}/profile/avatar/upload
func (*ProjectsService) Get ¶
func (s *ProjectsService) Get(ctx context.Context) (*Project, error)
Get fetches the project's display name, slug, and profile.
GET /projects/{projectId}/
func (*ProjectsService) GetProfile ¶
func (s *ProjectsService) GetProfile(ctx context.Context) (*ProjectProfile, error)
GetProfile fetches the project profile.
GET /projects/{projectId}/profile
func (*ProjectsService) GetProfileSyncStatus ¶
func (s *ProjectsService) GetProfileSyncStatus(ctx context.Context) (*ProfileSyncStatus, error)
GetProfileSyncStatus returns current aggregate line convergence.
GET /projects/{projectId}/profile/sync
func (*ProjectsService) SyncProfile ¶
func (s *ProjectsService) SyncProfile(ctx context.Context) (*ProfileSyncResult, error)
SyncProfile idempotently aligns every active dedicated iMessage line profile to the project profile.
POST /projects/{projectId}/profile/sync
func (*ProjectsService) UpdateProfile ¶
func (s *ProjectsService) UpdateProfile(ctx context.Context, req UpdateProfileRequest) (*ProjectProfile, error)
UpdateProfile updates the supplied project name fields and conditionally propagates the complete profile to lines that still match the old project profile.
PATCH /projects/{projectId}/profile
func (*ProjectsService) UpdateSlug ¶
UpdateSlug replaces the project's slug. Slugs are 1–10 characters of lowercase letters, digits, and hyphens, with no leading or trailing hyphen (`^[a-z0-9](?:[a-z0-9-]{0,8}[a-z0-9])?$`). Invalid formats return 422; a slug owned by another active project returns 409.
PATCH /projects/{projectId}/slug/
func (*ProjectsService) UploadAvatar ¶
func (s *ProjectsService) UploadAvatar(ctx context.Context, contentType string, image io.Reader) (string, error)
UploadAvatar sets the project avatar in one call: it requests a presigned upload slot, PUTs the image bytes, and commits the key. contentType is the image MIME type, e.g. "image/png". It returns the public avatar URL.
type RegisteredWebhook ¶
RegisteredWebhook is the result of Register. SigningSecret (64 lowercase hex characters) is returned only here and can never be retrieved again — store it in your secrets manager immediately. If you lose it, delete the webhook and register the URL again.
type RoutedLine ¶
type RoutedLine struct {
Line IMessageLine `json:"line"`
// IsBestAvailable is false when the returned line is the
// least-bad fallback — it holds more than 500 users or grew by
// more than 10 users in the last minute, meaning no genuinely
// healthy line was available.
IsBestAvailable bool `json:"isBestAvailable"`
}
RoutedLine is the result of Route.
type SIPInboundConfig ¶
type SIPInboundConfig struct {
ConfigID string `json:"configId"`
ProjectID string `json:"projectId"`
SIPURI string `json:"sipUri"`
Username *string `json:"username"`
HasPassword bool `json:"hasPassword"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
SIPInboundConfig is the project's SIP inbound configuration. The password is never returned; HasPassword indicates whether one is set.
type SimplePlatform ¶
type SimplePlatform struct {
Enabled bool `json:"enabled"`
}
SimplePlatform is a platform entry with no extra metadata.
type SlackAppConfig ¶
type SlackAppConfig struct {
AppConfigID string `json:"appConfigId"`
ProjectID string `json:"projectId"`
EnabledFeatures []string `json:"enabledFeatures"`
ClientID *string `json:"clientId"`
ClientSecret *string `json:"clientSecret"`
SigningSecret *string `json:"signingSecret"`
AppID *string `json:"appId"`
InstallationCount int `json:"installationCount"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
SlackAppConfig is the project's Slack app configuration.
The GET and PUT endpoints return plaintext credentials (ClientSecret, SigningSecret) — only call them from trusted environments.
type SlackInstallation ¶
type SlackInstallation struct {
InstallationID string `json:"installationId"`
AppConfigID string `json:"appConfigId"`
ProjectID string `json:"projectId"`
TeamID string `json:"teamId"`
TeamName string `json:"teamName"`
AppID string `json:"appId"`
BotToken string `json:"botToken"`
BotRefreshToken *string `json:"botRefreshToken"`
BotTokenExpiresAt *string `json:"botTokenExpiresAt"`
BotUserID string `json:"botUserId"`
GrantedScopes []string `json:"grantedScopes"`
InstalledAt string `json:"installedAt"`
UpdatedAt string `json:"updatedAt"`
}
SlackInstallation is one workspace installation of the project's Slack app.
The list and upsert endpoints return plaintext bot tokens — only call them from trusted environments.
type SlackService ¶
type SlackService service
SlackService manages the project's Slack app configuration and workspace installations and issues runtime tokens.
func (*SlackService) DeleteAppConfig ¶
func (s *SlackService) DeleteAppConfig(ctx context.Context) error
DeleteAppConfig soft-deletes the project's active Slack app configuration. Existing installations are not deleted — they keep referencing the soft-deleted config until the project is deleted or the installations are explicitly removed.
DELETE /projects/{projectId}/slack/
func (*SlackService) DeleteInstallation ¶
func (s *SlackService) DeleteInstallation(ctx context.Context, teamID string) error
DeleteInstallation soft-deletes a workspace installation, freeing the (app, team) slot so the same workspace can re-install via OAuth.
DELETE /projects/{projectId}/slack/installations/{teamId}
func (*SlackService) GetAppConfig ¶
func (s *SlackService) GetAppConfig(ctx context.Context) (*SlackAppConfig, error)
GetAppConfig returns the project's active Slack app configuration plus the count of active installations. Returns a 404 *Error if no active config exists.
GET /projects/{projectId}/slack/
func (*SlackService) IssueTokens ¶
func (s *SlackService) IssueTokens(ctx context.Context) (*SlackTokens, error)
IssueTokens issues per-installation Slack LightAuth tokens for the project.
POST /projects/{projectId}/slack/tokens
func (*SlackService) ListInstallations ¶
func (s *SlackService) ListInstallations(ctx context.Context) ([]SlackInstallation, error)
ListInstallations returns every active installation owned by the project's active Slack app config.
GET /projects/{projectId}/slack/installations
func (*SlackService) Setup ¶
func (s *SlackService) Setup(ctx context.Context, req SlackSetupRequest) (*SlackSetupResult, error)
Setup creates (or looks up) the project's Slack app by forwarding to Slack's app-manifest API. Use this when the dashboard OAuth proxy flow isn't available.
POST /projects/{projectId}/slack/setup
func (*SlackService) UpsertAppConfig ¶
func (s *SlackService) UpsertAppConfig(ctx context.Context, req UpsertSlackAppConfigRequest) (*SlackAppConfig, error)
UpsertAppConfig creates or updates the project's Slack app configuration.
PUT /projects/{projectId}/slack/
func (*SlackService) UpsertInstallation ¶
func (s *SlackService) UpsertInstallation(ctx context.Context, teamID string, req UpsertSlackInstallationRequest) (*SlackInstallation, error)
UpsertInstallation creates or updates an installation row for a workspace. Returns a 409 *Error if the project has no active Slack app config — call UpsertAppConfig (or Setup) first.
PUT /projects/{projectId}/slack/installations/{teamId}
type SlackSetupRequest ¶
type SlackSetupRequest struct {
AppName string `json:"appName"`
EnabledFeatures []string `json:"enabledFeatures"`
ConfigToken string `json:"configToken,omitempty"`
RefreshToken string `json:"refreshToken,omitempty"`
}
SlackSetupRequest creates (or looks up) the project's Slack app via a Slack workspace-admin config token (xoxe.xoxp-…), required on first install. RefreshToken is the optional paired token (xoxe-…); when supplied it is persisted so the app manifest can be auto-updated later.
type SlackSetupResult ¶
SlackSetupResult is the acknowledgement of Setup. AppID is nil when Slack did not return one.
type SlackTeam ¶
type SlackTeam struct {
TeamName string `json:"teamName"`
BotUserID string `json:"botUserId"`
AppID string `json:"appId"`
GrantedScopes []string `json:"grantedScopes"`
}
SlackTeam describes one installed workspace in SlackTokens.
type SlackTokens ¶
type SlackTokens struct {
Auth map[string]string `json:"auth"`
Teams map[string]SlackTeam `json:"teams"`
ExpiresIn int `json:"expiresIn"`
}
SlackTokens is the result of IssueTokens. Auth maps Slack team_id → LightAuth token and Teams maps team_id → workspace details; the map covers every workspace this deployment can act on. All tokens share the same TTL in seconds.
type Subscription ¶
type Subscription struct {
Tier string `json:"tier"`
// Status is nil when the project has no subscription.
Status *SubscriptionStatus `json:"status"`
CancelAtPeriodEnd bool `json:"cancel_at_period_end"`
SubscriptionID *string `json:"subscription_id"`
CustomerID *string `json:"customer_id"`
}
Subscription is the project's current plan tier and subscription state.
type SubscriptionStatus ¶
type SubscriptionStatus string
SubscriptionStatus is a Stripe-side subscription state.
const ( SubscriptionActive SubscriptionStatus = "active" SubscriptionCanceled SubscriptionStatus = "canceled" SubscriptionPastDue SubscriptionStatus = "past_due" )
type Template ¶
type Template struct {
ID string `json:"id"`
Name string `json:"name"`
Status TemplateStatus `json:"status"`
Category TemplateCategory `json:"category"`
Language string `json:"language"`
Components []TemplateComponent `json:"components"`
ParameterFormat TemplateParameterFormat `json:"parameterFormat,omitempty"`
QualityScore *TemplateQualityScore `json:"qualityScore,omitempty"`
RejectedReason string `json:"rejectedReason,omitempty"`
}
Template is a WhatsApp Business message template.
type TemplateCategory ¶
type TemplateCategory string
TemplateCategory is a Meta message-template category.
const ( TemplateMarketing TemplateCategory = "MARKETING" TemplateUtility TemplateCategory = "UTILITY" TemplateAuthentication TemplateCategory = "AUTHENTICATION" )
type TemplateComponent ¶
TemplateComponent is one entry of a template's `components` array in Meta's snake_case shape, forwarded unchanged. It must contain at least a "type" key. See Meta's components reference: https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates
type TemplateList ¶
type TemplateList struct {
Templates []Template `json:"templates"`
Paging TemplatePaging `json:"paging"`
}
TemplateList is one page of templates.
type TemplatePaging ¶
type TemplatePaging struct {
NextCursor *string `json:"nextCursor"`
PrevCursor *string `json:"prevCursor"`
}
TemplatePaging carries Meta's opaque paging cursors.
type TemplateParameterFormat ¶
type TemplateParameterFormat string
TemplateParameterFormat is how a template's variables are addressed.
const ( TemplatePositional TemplateParameterFormat = "POSITIONAL" TemplateNamed TemplateParameterFormat = "NAMED" )
type TemplateQualityScore ¶
type TemplateQualityScore struct {
Score string `json:"score"`
}
TemplateQualityScore is Meta's quality assessment of a template.
type TemplateStatus ¶
type TemplateStatus string
TemplateStatus is a Meta message-template lifecycle state.
const ( TemplateApproved TemplateStatus = "APPROVED" TemplatePending TemplateStatus = "PENDING" TemplateRejected TemplateStatus = "REJECTED" TemplatePaused TemplateStatus = "PAUSED" TemplateInAppeal TemplateStatus = "IN_APPEAL" TemplatePendingDeletion TemplateStatus = "PENDING_DELETION" TemplateDeleted TemplateStatus = "DELETED" TemplateDisabled TemplateStatus = "DISABLED" TemplateLimitExceeded TemplateStatus = "LIMIT_EXCEEDED" )
type UpdateLineProfileRequest ¶
type UpdateLineProfileRequest struct {
FirstName Nullable[string] `json:"firstName,omitzero"`
LastName Nullable[string] `json:"lastName,omitzero"`
}
UpdateLineProfileRequest merges name fields into a line's stored profile. Unset fields are preserved; explicit nulls clear them.
type UpdateProfileRequest ¶
type UpdateProfileRequest struct {
FirstName *string `json:"firstName,omitempty"`
LastName *string `json:"lastName,omitempty"`
}
UpdateProfileRequest carries the name fields to update. Nil fields are left unchanged.
type UpsertSIPInboundRequest ¶
type UpsertSIPInboundRequest struct {
SIPURI string `json:"sipUri,omitempty"`
Username Nullable[string] `json:"username,omitzero"`
Password Nullable[string] `json:"password,omitzero"`
}
UpsertSIPInboundRequest creates or patches the SIP inbound config.
On first call (no active config), SIPURI is required and the project's voice platform must be enabled. On subsequent calls any non-empty subset patches the existing config: unset fields are preserved, and explicit nulls (spectrum.Null[string]()) clear Username/Password. The resulting state must have Username and Password either both set or both null — half-credentials are rejected.
type UpsertSlackAppConfigRequest ¶
type UpsertSlackAppConfigRequest struct {
EnabledFeatures []string `json:"enabledFeatures,omitempty"`
ClientID string `json:"clientId,omitempty"`
ClientSecret string `json:"clientSecret,omitempty"`
SigningSecret string `json:"signingSecret,omitempty"`
AppID string `json:"appId,omitempty"`
}
UpsertSlackAppConfigRequest creates or updates the Slack app configuration. The update is partial: zero-valued fields are preserved. EnabledFeatures is validated against the server's feature catalog.
Never pass a Slack config token (xoxe.xoxp-…) here — that's a workspace-admin credential and must not be persisted; use Setup for the config-token flow instead.
type UpsertSlackInstallationRequest ¶
type UpsertSlackInstallationRequest struct {
TeamName string `json:"teamName"`
AppID string `json:"appId"`
BotToken string `json:"botToken"`
BotRefreshToken string `json:"botRefreshToken,omitempty"`
BotTokenExpiresInSec int `json:"botTokenExpiresInSec,omitempty"`
BotUserID string `json:"botUserId"`
GrantedScopes []string `json:"grantedScopes"`
}
UpsertSlackInstallationRequest creates or updates an installation row for a workspace, typically after Slack's oauth.v2.access succeeds.
type User ¶
type User struct {
ID string `json:"id"`
ProjectID string `json:"projectId"`
Type UserType `json:"type"`
FirstName *string `json:"firstName"`
LastName *string `json:"lastName"`
Email *string `json:"email"`
// PhoneNumber is the user's own number (E.164).
PhoneNumber string `json:"phoneNumber"`
// AssignedPhoneNumber is the project line the user messages
// (E.164).
AssignedPhoneNumber string `json:"assignedPhoneNumber"`
Meta map[string]any `json:"meta"`
CreatedAt string `json:"createdAt"`
}
User is a project user.
type UsersService ¶
type UsersService service
UsersService manages the project's users — the people your agent talks to.
func (*UsersService) Create ¶
func (s *UsersService) Create(ctx context.Context, req CreateUserRequest) (*User, error)
Create creates (or, for an existing active shared phone number, updates) a user.
POST /projects/{projectId}/users/
func (*UsersService) Delete ¶
func (s *UsersService) Delete(ctx context.Context, userID string) error
Delete soft-deletes a user. The user no longer appears in listings; a shared user's assigned number can be reused if they are re-created later.
DELETE /projects/{projectId}/users/{userId}/
func (*UsersService) Get ¶
Get returns a single user by id.
GET /projects/{projectId}/users/{userId}/
func (*UsersService) List ¶
func (s *UsersService) List(ctx context.Context, opts *ListUsersOptions) (*UserList, error)
List returns active users for the project.
GET /projects/{projectId}/users/
func (*UsersService) RedirectURL ¶
func (s *UsersService) RedirectURL(userID, msg string) string
RedirectURL returns the public URL that redirects a shared user to the appropriate messaging platform (currently iMessage via an sms: deep link). msg optionally overrides the default message body; pass "" to use the default. Hand this URL to the end user — it requires no authentication.
GET /users/{userId}/redirect
func (*UsersService) ResolveRedirect ¶
ResolveRedirect calls the public redirect endpoint without following it and returns the platform deep link it points to (for example an sms: URL). Errors mirror the endpoint's documented responses: 403 when the user is not shared or the platform is disabled, 404 when the user is unknown, 422 when the user has no assigned number.
type VoicePlatform ¶
type VoicePlatform struct {
Enabled bool `json:"enabled"`
// IMessageEnabled controls whether voice is reachable from
// iMessage.
IMessageEnabled bool `json:"imessage_enabled"`
}
VoicePlatform is the voice platform entry.
type VoiceService ¶
type VoiceService service
VoiceService manages the project's SIP inbound configuration and issues voice runtime tokens.
func (*VoiceService) DeleteSIPInbound ¶
func (s *VoiceService) DeleteSIPInbound(ctx context.Context) error
DeleteSIPInbound soft-deletes the project's active SIP inbound configuration. Returns a 404 *Error if no active config exists. After deletion a fresh config can be created via UpsertSIPInbound.
DELETE /projects/{projectId}/voice/sip-inbound/
func (*VoiceService) GetSIPInbound ¶
func (s *VoiceService) GetSIPInbound(ctx context.Context) (*SIPInboundConfig, error)
GetSIPInbound returns the project's active SIP inbound configuration, or nil if none is configured.
GET /projects/{projectId}/voice/sip-inbound/
func (*VoiceService) IssueToken ¶
func (s *VoiceService) IssueToken(ctx context.Context) (*Token, error)
IssueToken issues a single voice LightAuth token for the project. One token is returned regardless of dedicated/shared provisioning.
POST /projects/{projectId}/voice/tokens
func (*VoiceService) UpsertSIPInbound ¶
func (s *VoiceService) UpsertSIPInbound(ctx context.Context, req UpsertSIPInboundRequest) (*SIPInboundConfig, error)
UpsertSIPInbound sets or updates the project's SIP inbound configuration.
PATCH /projects/{projectId}/voice/sip-inbound/
type Webhook ¶
type Webhook struct {
ID string `json:"id"`
WebhookURL string `json:"webhookUrl"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
Webhook is a registered delivery destination.
type WebhooksService ¶
type WebhooksService service
WebhooksService registers, lists, and deletes the project's webhook destinations. To verify and decode deliveries arriving at those destinations, use package github.com/datacatcorp/gospectrum/webhook.
func (*WebhooksService) Delete ¶
func (s *WebhooksService) Delete(ctx context.Context, webhookID string) error
Delete stops delivery to a webhook. Once this call returns, no further events are sent to its URL (a delivery already in flight may still complete), and the webhook's signing secret is invalidated.
To rotate a signing secret without dropping deliveries: Register the same URL again (obtaining a new id and secret), deploy the new secret, then Delete the old webhook id.
DELETE /projects/{projectId}/webhooks/{webhookId}
func (*WebhooksService) List ¶
func (s *WebhooksService) List(ctx context.Context) ([]Webhook, error)
List returns the webhooks currently registered for the project, ordered by creation time (oldest first). Signing secrets are never included in list responses.
GET /projects/{projectId}/webhooks/
func (*WebhooksService) Register ¶
func (s *WebhooksService) Register(ctx context.Context, webhookURL string) (*RegisteredWebhook, error)
Register adds a destination URL for the project. The URL must be a public HTTPS endpoint — the delivery worker won't POST to plain http://, private/internal addresses, or through redirects.
Every inbound message for the project is then delivered to the URL as a signed JSON POST; each registered URL receives every event independently.
POST /projects/{projectId}/webhooks/
type WhatsAppAccount ¶
type WhatsAppAccount struct {
AccountID string `json:"accountId"`
WABAID string `json:"wabaId"`
BusinessName *string `json:"businessName"`
CreatedAt string `json:"createdAt"`
}
WhatsAppAccount is an onboarded WhatsApp Business account (WABA). AccountID scopes the template endpoints; WABAID is Meta's id.
type WhatsAppLine ¶
type WhatsAppLine struct {
State WhatsAppLineState `json:"state"`
ID string `json:"id,omitempty"`
PhoneNumberID string `json:"phoneNumberId"`
DisplayPhoneNumber *string `json:"displayPhoneNumber"`
CreatedAt string `json:"createdAt"`
// Registered-only fields.
VerifiedName *string `json:"verifiedName,omitempty"`
QualityRating *string `json:"qualityRating,omitempty"`
Status *string `json:"status,omitempty"`
CodeVerificationStatus *string `json:"codeVerificationStatus,omitempty"`
WABAID string `json:"wabaId,omitempty"`
BusinessName *string `json:"businessName,omitempty"`
// Pending-only fields.
RegistrationState WhatsAppRegistrationState `json:"registrationState,omitempty"`
ErrorCode *string `json:"errorCode,omitempty"`
ErrorMessage *string `json:"errorMessage,omitempty"`
}
WhatsAppLine is a WhatsApp Business line. State selects which fields are populated: a registered line carries ID, WABAID, and the quality fields; a pending one carries RegistrationState and the error fields.
type WhatsAppLineState ¶
type WhatsAppLineState string
WhatsAppLineState distinguishes registered lines from ones still moving through Meta registration.
const ( WhatsAppLineRegistered WhatsAppLineState = "registered" WhatsAppLinePending WhatsAppLineState = "pending" )
type WhatsAppRegistrationState ¶
type WhatsAppRegistrationState string
WhatsAppRegistrationState is the progress of a pending WhatsApp Business line registration.
const ( WhatsAppRegistering WhatsAppRegistrationState = "registering" WhatsAppRegistrationFailed WhatsAppRegistrationState = "failed" )
type WhatsAppService ¶
type WhatsAppService service
WhatsAppService manages WhatsApp Business accounts and message templates and issues runtime tokens.
func (*WhatsAppService) CreateTemplate ¶
func (s *WhatsAppService) CreateTemplate(ctx context.Context, accountID string, req CreateTemplateRequest) (*CreatedTemplate, error)
CreateTemplate creates a message template under the given WhatsApp Business account.
POST /projects/{projectId}/whatsapp-business/accounts/{accountId}/templates/
func (*WhatsAppService) DeleteTemplate ¶
func (s *WhatsAppService) DeleteTemplate(ctx context.Context, accountID, templateID, name string) error
DeleteTemplate deletes a single language version of a message template. templateID is forwarded to Meta as hsm_id so the delete is scoped to that exact template row; name is required because Meta's delete endpoint requires it alongside hsm_id. Other language versions sharing the same name are untouched.
DELETE /projects/{projectId}/whatsapp-business/accounts/{accountId}/templates/{templateId}
func (*WhatsAppService) EditTemplate ¶
func (s *WhatsAppService) EditTemplate(ctx context.Context, accountID, templateID string, req EditTemplateRequest) error
EditTemplate edits an existing message template.
PATCH /projects/{projectId}/whatsapp-business/accounts/{accountId}/templates/{templateId}
func (*WhatsAppService) IssueTokens ¶
func (s *WhatsAppService) IssueTokens(ctx context.Context) (*WhatsAppTokens, error)
IssueTokens issues per-line WhatsApp Business LightAuth tokens for the project.
POST /projects/{projectId}/whatsapp-business/tokens
func (*WhatsAppService) ListAccounts ¶
func (s *WhatsAppService) ListAccounts(ctx context.Context) ([]WhatsAppAccount, error)
ListAccounts lists the project's onboarded WhatsApp Business accounts, newest first. Returns an empty slice if the project has none.
GET /projects/{projectId}/whatsapp-business/accounts
func (*WhatsAppService) ListTemplates ¶
func (s *WhatsAppService) ListTemplates(ctx context.Context, accountID string, opts ListTemplatesOptions) (*TemplateList, error)
ListTemplates lists message templates for a WhatsApp Business account. Component JSON is preserved in Meta's snake_case shape.
GET /projects/{projectId}/whatsapp-business/accounts/{accountId}/templates/
type WhatsAppTokens ¶
type WhatsAppTokens struct {
Auth map[string]string `json:"auth"`
Numbers map[string]*string `json:"numbers"`
ExpiresIn int `json:"expiresIn"`
}
WhatsAppTokens is the result of IssueTokens. Auth maps Meta phone_number_id → LightAuth token; Numbers maps phone_number_id → display phone number (nil when Meta has none on file). All tokens share the same TTL in seconds.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package dashboard is a client for the Photon Dashboard API — the authenticated REST API behind app.photon.codes, used by the web app and the photon CLI for project CRUD and device login.
|
Package dashboard is a client for the Photon Dashboard API — the authenticated REST API behind app.photon.codes, used by the web app and the photon CLI for project CRUD and device login. |
|
examples
|
|
|
testbot
command
Command testbot is the Go equivalent of the app scaffolded by `npm create spectrum-project -- --providers imessage`: an iMessage echo bot on the gospectrum library.
|
Command testbot is the Go equivalent of the app scaffolded by `npm create spectrum-project -- --providers imessage`: an iMessage echo bot on the gospectrum library. |
|
Package imessage is a Go client for Photon's iMessage runtime — the gRPC surface behind the official @photon-ai/advanced-imessage SDK.
|
Package imessage is a Go client for Photon's iMessage runtime — the gRPC surface behind the official @photon-ai/advanced-imessage SDK. |
|
internal
|
|
|
grpcx
Package grpcx holds the transport behaviour shared by the Photon runtime gRPC clients (iMessage, WhatsApp Business, Slack): channel defaults, bearer/metadata auth, idempotency keys, and the x-retryable retry contract.
|
Package grpcx holds the transport behaviour shared by the Photon runtime gRPC clients (iMessage, WhatsApp Business, Slack): channel defaults, bearer/metadata auth, idempotency keys, and the x-retryable retry contract. |
|
tokencache
Package tokencache caches short-lived runtime tokens minted by the Spectrum management API, refreshing them before expiry.
|
Package tokencache caches short-lived runtime tokens minted by the Spectrum management API, refreshing them before expiry. |
|
Package slack is a Go client for Photon's Slack runtime — the gRPC surface behind the official @photon-ai/slack SDK.
|
Package slack is a Go client for Photon's Slack runtime — the gRPC surface behind the official @photon-ai/slack SDK. |
|
Package webhook verifies and decodes Spectrum webhook deliveries.
|
Package webhook verifies and decodes Spectrum webhook deliveries. |
|
Package whatsapp is a Go client for Photon's WhatsApp Business runtime — the gRPC surface behind the official @photon-ai/whatsapp-business SDK.
|
Package whatsapp is a Go client for Photon's WhatsApp Business runtime — the gRPC surface behind the official @photon-ai/whatsapp-business SDK. |