@proofchain/sdk 2.22.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -16,6 +16,7 @@ declare class HttpClient {
16
16
  private baseUrl;
17
17
  private timeout;
18
18
  private maxRetries;
19
+ private readonly inflight;
19
20
  constructor(options: HttpClientOptions);
20
21
  private getHeaders;
21
22
  private handleResponse;
@@ -2238,7 +2239,7 @@ interface QuestStep {
2238
2239
  name: string;
2239
2240
  description?: string;
2240
2241
  order: number;
2241
- step_type: 'event' | 'manual' | 'external' | 'compound';
2242
+ step_type: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
2242
2243
  event_type?: string;
2243
2244
  event_types?: string[];
2244
2245
  criteria?: Record<string, any>;
@@ -2354,7 +2355,7 @@ interface CreateQuestStepRequest {
2354
2355
  name: string;
2355
2356
  description?: string;
2356
2357
  order?: number;
2357
- step_type?: 'event' | 'manual' | 'external' | 'compound';
2358
+ step_type?: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
2358
2359
  event_type?: string;
2359
2360
  event_types?: string[];
2360
2361
  criteria?: Record<string, any>;
@@ -2394,9 +2395,10 @@ declare class QuestsClient {
2394
2395
  category?: string;
2395
2396
  }): Promise<QuestWithProgress[]>;
2396
2397
  /**
2397
- * Get a quest by slug
2398
+ * REMOVED: getBySlug() the server never implemented GET /quests/slug/{slug}.
2399
+ * There is no slug-based lookup route in the API. Use list() with a status/category
2400
+ * filter and find the quest client-side, or use get() with the quest ID.
2398
2401
  */
2399
- getBySlug(slug: string): Promise<Quest>;
2400
2402
  /**
2401
2403
  * Create a quest
2402
2404
  */
@@ -2422,9 +2424,9 @@ declare class QuestsClient {
2422
2424
  */
2423
2425
  pause(questId: string): Promise<Quest>;
2424
2426
  /**
2425
- * Archive a quest
2427
+ * REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
2428
+ * To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
2426
2429
  */
2427
- archive(questId: string): Promise<Quest>;
2428
2430
  /**
2429
2431
  * Get quest with user progress
2430
2432
  * Fetches the quest and user's progress separately and combines them
@@ -2469,19 +2471,37 @@ declare class QuestsClient {
2469
2471
  */
2470
2472
  claimReward(questId: string, userId: string): Promise<ClaimRewardResult>;
2471
2473
  /**
2472
- * Add a step to a quest
2474
+ * Add a step to a quest.
2475
+ *
2476
+ * The server has no dedicated step-create endpoint — steps are managed via the
2477
+ * full-replace PUT /quests/{id}. This method fetches the quest, appends the new
2478
+ * step, and issues a full update. The returned QuestStep is synthesised from the
2479
+ * updated quest's steps array (matched by order or last position).
2473
2480
  */
2474
2481
  addStep(questId: string, step: CreateQuestStepRequest): Promise<QuestStep>;
2475
2482
  /**
2476
- * Update a step
2483
+ * Update a step by step ID.
2484
+ *
2485
+ * The server has no dedicated step-update endpoint — steps are managed via the
2486
+ * full-replace PUT /quests/{id}. This method fetches the quest, replaces the
2487
+ * matching step, and issues a full update.
2477
2488
  */
2478
2489
  updateStep(questId: string, stepId: string, data: Partial<CreateQuestStepRequest>): Promise<QuestStep>;
2479
2490
  /**
2480
- * Delete a step
2491
+ * Delete a step by step ID.
2492
+ *
2493
+ * The server has no dedicated step-delete endpoint — steps are managed via the
2494
+ * full-replace PUT /quests/{id}. This method fetches the quest, filters out the
2495
+ * target step, and issues a full update.
2481
2496
  */
2482
2497
  deleteStep(questId: string, stepId: string): Promise<void>;
2483
2498
  /**
2484
- * Reorder steps
2499
+ * Reorder steps by providing step IDs in the desired order.
2500
+ *
2501
+ * The server has no dedicated reorder endpoint — steps are managed via the
2502
+ * full-replace PUT /quests/{id}. This method fetches the quest, reassigns
2503
+ * the `order` field according to the given stepIds sequence, and issues a
2504
+ * full update.
2485
2505
  */
2486
2506
  reorderSteps(questId: string, stepIds: string[]): Promise<Quest>;
2487
2507
  }
@@ -3520,12 +3540,29 @@ declare class NftClient {
3520
3540
 
3521
3541
  /**
3522
3542
  * Partner Keys Resource - OTT (One-Time Token) auth for partner integrations
3543
+ *
3544
+ * Flow:
3545
+ * 1. Mobile app generates OTT: POST /partner-keys/generate (tenant API key + user JWT)
3546
+ * 2. OTT is passed to partner webview via URL param
3547
+ * 3. Partner backend redeems OTT: POST /partner-keys/redeem (integrator API key)
3523
3548
  */
3524
3549
 
3525
- interface OTTRequestResponse {
3550
+ interface OTTGenerateRequest {
3551
+ ttl?: number;
3552
+ session_data_keys?: string[];
3553
+ }
3554
+ interface OTTGenerateResponse {
3526
3555
  ott: string;
3527
3556
  expires_in: number;
3528
3557
  }
3558
+ interface OTTRedeemResponse {
3559
+ user_id: string;
3560
+ tenant_id: string;
3561
+ tenant_name: string | null;
3562
+ session_data: Record<string, unknown>;
3563
+ issued_at: string;
3564
+ expires_at: string;
3565
+ }
3529
3566
  interface OTTConfigUpdate {
3530
3567
  ott_enabled?: boolean;
3531
3568
  ott_ttl_seconds?: number;
@@ -3542,21 +3579,40 @@ interface OTTConfigResponse {
3542
3579
  ott_jwt_ttl_seconds: number;
3543
3580
  ott_session_data_keys?: string[];
3544
3581
  }
3582
+ /** @deprecated Use OTTGenerateResponse */
3583
+ type OTTRequestResponse = OTTGenerateResponse;
3545
3584
  declare class PartnerKeysClient {
3546
3585
  private http;
3547
3586
  constructor(http: HttpClient);
3548
3587
  /**
3549
- * Request a one-time token for a partner key (end-user JWKS auth).
3588
+ * Generate a one-time token for the authenticated end-user.
3589
+ *
3590
+ * Requires: tenant API key (X-API-Key) + end-user JWT (Authorization: Bearer).
3591
+ * The SDK must be initialised with both `apiKey` and `userToken`.
3592
+ *
3593
+ * @param options.ttl - Token lifetime in seconds (30–3600, default 300)
3594
+ * @param options.session_data_keys - Whitelist of JWT claims to include
3595
+ */
3596
+ generate(options?: OTTGenerateRequest): Promise<OTTGenerateResponse>;
3597
+ /**
3598
+ * Redeem a one-time token to get the user's identity.
3599
+ *
3600
+ * Requires: integrator API key (X-API-Key). The token is single-use
3601
+ * and deleted on successful redemption.
3602
+ *
3603
+ * @param ott - The opaque one-time token
3550
3604
  */
3551
- requestOTT(keyId: string): Promise<OTTRequestResponse>;
3605
+ redeem(ott: string): Promise<OTTRedeemResponse>;
3552
3606
  /**
3553
- * Update OTT configuration for a partner key.
3607
+ * Update OTT configuration for a partner key (tenant admin).
3554
3608
  */
3555
3609
  updateOTTConfig(keyId: string, config: OTTConfigUpdate): Promise<OTTConfigResponse>;
3556
3610
  /**
3557
- * Get OTT configuration for a partner key.
3611
+ * Get OTT configuration for a partner key (tenant admin).
3558
3612
  */
3559
3613
  getOTTConfig(keyId: string): Promise<OTTConfigResponse>;
3614
+ /** @deprecated Use generate() */
3615
+ requestOTT(_keyId: string): Promise<OTTGenerateResponse>;
3560
3616
  }
3561
3617
 
3562
3618
  /**
@@ -4150,4 +4206,4 @@ declare class TimeoutError extends ProofChainError {
4150
4206
  constructor(message?: string);
4151
4207
  }
4152
4208
 
4153
- export { type Achievement, type ActivitySummaryView, type AddNFTRequest, type AnchorResult, type ApiKey, type AttestRequest, type AttestationConfig, type AttestationMode, type Attestation as AttestationRecord, type AttestationResult, type AttestationSchema, AttestationsClient, AuthenticationError, AuthorizationError, type AvailableView, type Badge, type BatchIngestRequest, type BatchIngestResponse, type BatchVerifyResult, type BlockchainProof, type BlockchainStats, type Certificate, type CertificateVerifyResult, CertificatesResource, type Channel, type ChannelState, type ChannelStatus, ChannelsResource, type ClaimNFTResult, type ClaimRewardResult, type CohortDefinition, type CohortGroupStats, CohortLeaderboardClient, type CohortLeaderboardEntry, type CohortLeaderboardOptions, type CohortLeaderboardResponse, type ComprehensiveWalletInfo, type CreateAchievementRequest, type CreateApiKeyRequest, type CreateAttestationRequest, type CreateBadgeRequest, type CreateChannelRequest, type CreateCredentialTypeRequest, type CreateDataViewRequest, type CreateDualWalletsRequest, type CreateEndUserRequest, type CreateEventRequest, type CreatePassportDefinitionRequest, type CreatePassportRequest, type CreateQuestRequest, type CreateQuestStepRequest, type CreateRewardDefinitionRequest, type CreateSchemaRequest, type CreateTemplateFieldRequest, type CreateTemplateRequest, type CreateWalletRequest as CreateUserWalletRequest, type CreateWalletRequest$1 as CreateWalletRequest, type CreateWebhookRequest, type CredentialType, type CredentialVerifyResult, CredentialsClient, type DataViewColumn, type DataViewComputation, type DataViewDetail, type DataViewExecuteResult, type DataViewInfo, type DataViewListResponse, type DataViewPreviewRequest, type DataViewPreviewResult, type DataViewSummary, DataViewsClient, type DistributeResult, DocumentsResource, type DualWallets, type EarnedReward, type EndUser, EndUserIngestionClient, type EndUserIngestionClientOptions, type EndUserListResponse, EndUsersClient, type Event, type EventBatchProof, type EventMetadata, type EventStatus, EventsResource, type ExecuteSwapRequest, type Facet, type FacetsResponse, type FanProfileView, type FanpassGroupStats, FanpassLeaderboardClient, type FanpassLeaderboardEntry, type FanpassLeaderboardOptions, type FanpassLeaderboardResponse, type FanpassUserComparisonResponse, type FieldValue, type GDPRDeletionRequest, type GDPRDeletionResponse, type GDPRPreviewResponse, type IngestEventRequest, type IngestEventResponse, IngestionClient, type IngestionClientOptions, type IssueCertificateRequest, type IssueCredentialRequest, type IssuedCredential, type LeaderboardUserProfile$1 as LeaderboardUserProfile, type LinkWalletRequest, type ListAttestationsOptions, type ListCertificatesRequest, type ListCohortsOptions, type ListEndUsersOptions, type ListEventsRequest, type ListIssuedCredentialsOptions, type ListMintsOptions, type ListMintsResponse, type ListQuestsOptions, type ListRewardsOptions, type ListSchemasOptions, type ListWalletCredentialsOptions, type ManualRewardRequest, type MergeUsersRequest, type Milestone, type MintNFTRequest, type NFT, type NFTMint, NetworkError, NftClient, NotFoundError, type NotificationCallback, type NotificationEvent, type NotificationEventType, NotificationsClient, type OTTConfigResponse, type OTTConfigUpdate, type OTTRequestResponse, PartnerKeysClient, type Passport, PassportClient, type PassportDefinition, type PassportFieldValue, type PassportHistory, type PassportTemplate, type PassportV2Data, type PassportWithFields, ProofChain, ProofChainError, type ProofChainOptions, type ProofVerifyResult, type PushSubscriptionJSON, type Quest, type QuestStep, type QuestWithProgress, QuestsClient, RateLimitError, type RegisterWalletRequest, type RevokeResult, type RewardAsset, type RewardAttestationResult, type RewardDefinition, type RewardEarned, type VerifyResult as RewardVerifyResult, RewardsClient, type Schema, type SchemaDetail, type SchemaField, type SchemaListResponse, type ValidationError$1 as SchemaValidationError, SchemasClient, type SearchFilters, type SearchQueryRequest, type SearchRequest, SearchResource, type SearchResponse, type SearchResult, type SearchStats, ServerError, type SetProfileRequest, type Settlement, type StepCompletionResult, type StepProgress, type StepStartResult, type StreamAck, type StreamEventRequest, type SubscribeOptions, type SwapQuote, type SwapQuoteRequest, type SwapResult, type TemplateField, type TenantInfo, TenantResource, type TierDefinition, TimeoutError, type TokenBalance, type TransferRequest, type TransferResult, type Unsubscribe, type UpdateConfigRequest as UpdateAttestationConfigRequest, type UpdateDataViewRequest, type UpdateEndUserRequest, type UpdatePassportRequest, type UpdateWebhookRequest, type UsageStats, type UserAchievement, type UserActivity, type UserActivityResponse, type UserBadge, type UserBreakdownResponse, type UserCohortBreakdownEntry, type UserCredentialsSummary, type UserQuestProgress, type UserReward$1 as UserReward, type UserRewardsResponse, type UserWalletSummary, type UserWithWallets, type UsersWithWalletsResponse, type ValidateDataRequest, ValidationError, type ValidationErrorDetail, type ValidationResult, type VaultFile, type VaultFolder, type VaultListResponse, VaultResource, type VaultStats, type VaultUploadRequest, type VerificationResult, VerifyResource, type ViewColumn, type ViewTemplate, type Wallet, type WalletBalance, WalletClient, type WalletCreationResult, type WalletCredential, type WalletCredentialVerifySummary, type WalletStats, type Webhook, WebhooksResource };
4209
+ export { type Achievement, type ActivitySummaryView, type AddNFTRequest, type AnchorResult, type ApiKey, type AttestRequest, type AttestationConfig, type AttestationMode, type Attestation as AttestationRecord, type AttestationResult, type AttestationSchema, AttestationsClient, AuthenticationError, AuthorizationError, type AvailableView, type Badge, type BatchIngestRequest, type BatchIngestResponse, type BatchVerifyResult, type BlockchainProof, type BlockchainStats, type Certificate, type CertificateVerifyResult, CertificatesResource, type Channel, type ChannelState, type ChannelStatus, ChannelsResource, type ClaimNFTResult, type ClaimRewardResult, type CohortDefinition, type CohortGroupStats, CohortLeaderboardClient, type CohortLeaderboardEntry, type CohortLeaderboardOptions, type CohortLeaderboardResponse, type ComprehensiveWalletInfo, type CreateAchievementRequest, type CreateApiKeyRequest, type CreateAttestationRequest, type CreateBadgeRequest, type CreateChannelRequest, type CreateCredentialTypeRequest, type CreateDataViewRequest, type CreateDualWalletsRequest, type CreateEndUserRequest, type CreateEventRequest, type CreatePassportDefinitionRequest, type CreatePassportRequest, type CreateQuestRequest, type CreateQuestStepRequest, type CreateRewardDefinitionRequest, type CreateSchemaRequest, type CreateTemplateFieldRequest, type CreateTemplateRequest, type CreateWalletRequest as CreateUserWalletRequest, type CreateWalletRequest$1 as CreateWalletRequest, type CreateWebhookRequest, type CredentialType, type CredentialVerifyResult, CredentialsClient, type DataViewColumn, type DataViewComputation, type DataViewDetail, type DataViewExecuteResult, type DataViewInfo, type DataViewListResponse, type DataViewPreviewRequest, type DataViewPreviewResult, type DataViewSummary, DataViewsClient, type DistributeResult, DocumentsResource, type DualWallets, type EarnedReward, type EndUser, EndUserIngestionClient, type EndUserIngestionClientOptions, type EndUserListResponse, EndUsersClient, type Event, type EventBatchProof, type EventMetadata, type EventStatus, EventsResource, type ExecuteSwapRequest, type Facet, type FacetsResponse, type FanProfileView, type FanpassGroupStats, FanpassLeaderboardClient, type FanpassLeaderboardEntry, type FanpassLeaderboardOptions, type FanpassLeaderboardResponse, type FanpassUserComparisonResponse, type FieldValue, type GDPRDeletionRequest, type GDPRDeletionResponse, type GDPRPreviewResponse, type IngestEventRequest, type IngestEventResponse, IngestionClient, type IngestionClientOptions, type IssueCertificateRequest, type IssueCredentialRequest, type IssuedCredential, type LeaderboardUserProfile$1 as LeaderboardUserProfile, type LinkWalletRequest, type ListAttestationsOptions, type ListCertificatesRequest, type ListCohortsOptions, type ListEndUsersOptions, type ListEventsRequest, type ListIssuedCredentialsOptions, type ListMintsOptions, type ListMintsResponse, type ListQuestsOptions, type ListRewardsOptions, type ListSchemasOptions, type ListWalletCredentialsOptions, type ManualRewardRequest, type MergeUsersRequest, type Milestone, type MintNFTRequest, type NFT, type NFTMint, NetworkError, NftClient, NotFoundError, type NotificationCallback, type NotificationEvent, type NotificationEventType, NotificationsClient, type OTTConfigResponse, type OTTConfigUpdate, type OTTGenerateRequest, type OTTGenerateResponse, type OTTRedeemResponse, type OTTRequestResponse, PartnerKeysClient, type Passport, PassportClient, type PassportDefinition, type PassportFieldValue, type PassportHistory, type PassportTemplate, type PassportV2Data, type PassportWithFields, ProofChain, ProofChainError, type ProofChainOptions, type ProofVerifyResult, type PushSubscriptionJSON, type Quest, type QuestStep, type QuestWithProgress, QuestsClient, RateLimitError, type RegisterWalletRequest, type RevokeResult, type RewardAsset, type RewardAttestationResult, type RewardDefinition, type RewardEarned, type VerifyResult as RewardVerifyResult, RewardsClient, type Schema, type SchemaDetail, type SchemaField, type SchemaListResponse, type ValidationError$1 as SchemaValidationError, SchemasClient, type SearchFilters, type SearchQueryRequest, type SearchRequest, SearchResource, type SearchResponse, type SearchResult, type SearchStats, ServerError, type SetProfileRequest, type Settlement, type StepCompletionResult, type StepProgress, type StepStartResult, type StreamAck, type StreamEventRequest, type SubscribeOptions, type SwapQuote, type SwapQuoteRequest, type SwapResult, type TemplateField, type TenantInfo, TenantResource, type TierDefinition, TimeoutError, type TokenBalance, type TransferRequest, type TransferResult, type Unsubscribe, type UpdateConfigRequest as UpdateAttestationConfigRequest, type UpdateDataViewRequest, type UpdateEndUserRequest, type UpdatePassportRequest, type UpdateWebhookRequest, type UsageStats, type UserAchievement, type UserActivity, type UserActivityResponse, type UserBadge, type UserBreakdownResponse, type UserCohortBreakdownEntry, type UserCredentialsSummary, type UserQuestProgress, type UserReward$1 as UserReward, type UserRewardsResponse, type UserWalletSummary, type UserWithWallets, type UsersWithWalletsResponse, type ValidateDataRequest, ValidationError, type ValidationErrorDetail, type ValidationResult, type VaultFile, type VaultFolder, type VaultListResponse, VaultResource, type VaultStats, type VaultUploadRequest, type VerificationResult, VerifyResource, type ViewColumn, type ViewTemplate, type Wallet, type WalletBalance, WalletClient, type WalletCreationResult, type WalletCredential, type WalletCredentialVerifySummary, type WalletStats, type Webhook, WebhooksResource };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@ declare class HttpClient {
16
16
  private baseUrl;
17
17
  private timeout;
18
18
  private maxRetries;
19
+ private readonly inflight;
19
20
  constructor(options: HttpClientOptions);
20
21
  private getHeaders;
21
22
  private handleResponse;
@@ -2238,7 +2239,7 @@ interface QuestStep {
2238
2239
  name: string;
2239
2240
  description?: string;
2240
2241
  order: number;
2241
- step_type: 'event' | 'manual' | 'external' | 'compound';
2242
+ step_type: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
2242
2243
  event_type?: string;
2243
2244
  event_types?: string[];
2244
2245
  criteria?: Record<string, any>;
@@ -2354,7 +2355,7 @@ interface CreateQuestStepRequest {
2354
2355
  name: string;
2355
2356
  description?: string;
2356
2357
  order?: number;
2357
- step_type?: 'event' | 'manual' | 'external' | 'compound';
2358
+ step_type?: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
2358
2359
  event_type?: string;
2359
2360
  event_types?: string[];
2360
2361
  criteria?: Record<string, any>;
@@ -2394,9 +2395,10 @@ declare class QuestsClient {
2394
2395
  category?: string;
2395
2396
  }): Promise<QuestWithProgress[]>;
2396
2397
  /**
2397
- * Get a quest by slug
2398
+ * REMOVED: getBySlug() the server never implemented GET /quests/slug/{slug}.
2399
+ * There is no slug-based lookup route in the API. Use list() with a status/category
2400
+ * filter and find the quest client-side, or use get() with the quest ID.
2398
2401
  */
2399
- getBySlug(slug: string): Promise<Quest>;
2400
2402
  /**
2401
2403
  * Create a quest
2402
2404
  */
@@ -2422,9 +2424,9 @@ declare class QuestsClient {
2422
2424
  */
2423
2425
  pause(questId: string): Promise<Quest>;
2424
2426
  /**
2425
- * Archive a quest
2427
+ * REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
2428
+ * To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
2426
2429
  */
2427
- archive(questId: string): Promise<Quest>;
2428
2430
  /**
2429
2431
  * Get quest with user progress
2430
2432
  * Fetches the quest and user's progress separately and combines them
@@ -2469,19 +2471,37 @@ declare class QuestsClient {
2469
2471
  */
2470
2472
  claimReward(questId: string, userId: string): Promise<ClaimRewardResult>;
2471
2473
  /**
2472
- * Add a step to a quest
2474
+ * Add a step to a quest.
2475
+ *
2476
+ * The server has no dedicated step-create endpoint — steps are managed via the
2477
+ * full-replace PUT /quests/{id}. This method fetches the quest, appends the new
2478
+ * step, and issues a full update. The returned QuestStep is synthesised from the
2479
+ * updated quest's steps array (matched by order or last position).
2473
2480
  */
2474
2481
  addStep(questId: string, step: CreateQuestStepRequest): Promise<QuestStep>;
2475
2482
  /**
2476
- * Update a step
2483
+ * Update a step by step ID.
2484
+ *
2485
+ * The server has no dedicated step-update endpoint — steps are managed via the
2486
+ * full-replace PUT /quests/{id}. This method fetches the quest, replaces the
2487
+ * matching step, and issues a full update.
2477
2488
  */
2478
2489
  updateStep(questId: string, stepId: string, data: Partial<CreateQuestStepRequest>): Promise<QuestStep>;
2479
2490
  /**
2480
- * Delete a step
2491
+ * Delete a step by step ID.
2492
+ *
2493
+ * The server has no dedicated step-delete endpoint — steps are managed via the
2494
+ * full-replace PUT /quests/{id}. This method fetches the quest, filters out the
2495
+ * target step, and issues a full update.
2481
2496
  */
2482
2497
  deleteStep(questId: string, stepId: string): Promise<void>;
2483
2498
  /**
2484
- * Reorder steps
2499
+ * Reorder steps by providing step IDs in the desired order.
2500
+ *
2501
+ * The server has no dedicated reorder endpoint — steps are managed via the
2502
+ * full-replace PUT /quests/{id}. This method fetches the quest, reassigns
2503
+ * the `order` field according to the given stepIds sequence, and issues a
2504
+ * full update.
2485
2505
  */
2486
2506
  reorderSteps(questId: string, stepIds: string[]): Promise<Quest>;
2487
2507
  }
@@ -3520,12 +3540,29 @@ declare class NftClient {
3520
3540
 
3521
3541
  /**
3522
3542
  * Partner Keys Resource - OTT (One-Time Token) auth for partner integrations
3543
+ *
3544
+ * Flow:
3545
+ * 1. Mobile app generates OTT: POST /partner-keys/generate (tenant API key + user JWT)
3546
+ * 2. OTT is passed to partner webview via URL param
3547
+ * 3. Partner backend redeems OTT: POST /partner-keys/redeem (integrator API key)
3523
3548
  */
3524
3549
 
3525
- interface OTTRequestResponse {
3550
+ interface OTTGenerateRequest {
3551
+ ttl?: number;
3552
+ session_data_keys?: string[];
3553
+ }
3554
+ interface OTTGenerateResponse {
3526
3555
  ott: string;
3527
3556
  expires_in: number;
3528
3557
  }
3558
+ interface OTTRedeemResponse {
3559
+ user_id: string;
3560
+ tenant_id: string;
3561
+ tenant_name: string | null;
3562
+ session_data: Record<string, unknown>;
3563
+ issued_at: string;
3564
+ expires_at: string;
3565
+ }
3529
3566
  interface OTTConfigUpdate {
3530
3567
  ott_enabled?: boolean;
3531
3568
  ott_ttl_seconds?: number;
@@ -3542,21 +3579,40 @@ interface OTTConfigResponse {
3542
3579
  ott_jwt_ttl_seconds: number;
3543
3580
  ott_session_data_keys?: string[];
3544
3581
  }
3582
+ /** @deprecated Use OTTGenerateResponse */
3583
+ type OTTRequestResponse = OTTGenerateResponse;
3545
3584
  declare class PartnerKeysClient {
3546
3585
  private http;
3547
3586
  constructor(http: HttpClient);
3548
3587
  /**
3549
- * Request a one-time token for a partner key (end-user JWKS auth).
3588
+ * Generate a one-time token for the authenticated end-user.
3589
+ *
3590
+ * Requires: tenant API key (X-API-Key) + end-user JWT (Authorization: Bearer).
3591
+ * The SDK must be initialised with both `apiKey` and `userToken`.
3592
+ *
3593
+ * @param options.ttl - Token lifetime in seconds (30–3600, default 300)
3594
+ * @param options.session_data_keys - Whitelist of JWT claims to include
3595
+ */
3596
+ generate(options?: OTTGenerateRequest): Promise<OTTGenerateResponse>;
3597
+ /**
3598
+ * Redeem a one-time token to get the user's identity.
3599
+ *
3600
+ * Requires: integrator API key (X-API-Key). The token is single-use
3601
+ * and deleted on successful redemption.
3602
+ *
3603
+ * @param ott - The opaque one-time token
3550
3604
  */
3551
- requestOTT(keyId: string): Promise<OTTRequestResponse>;
3605
+ redeem(ott: string): Promise<OTTRedeemResponse>;
3552
3606
  /**
3553
- * Update OTT configuration for a partner key.
3607
+ * Update OTT configuration for a partner key (tenant admin).
3554
3608
  */
3555
3609
  updateOTTConfig(keyId: string, config: OTTConfigUpdate): Promise<OTTConfigResponse>;
3556
3610
  /**
3557
- * Get OTT configuration for a partner key.
3611
+ * Get OTT configuration for a partner key (tenant admin).
3558
3612
  */
3559
3613
  getOTTConfig(keyId: string): Promise<OTTConfigResponse>;
3614
+ /** @deprecated Use generate() */
3615
+ requestOTT(_keyId: string): Promise<OTTGenerateResponse>;
3560
3616
  }
3561
3617
 
3562
3618
  /**
@@ -4150,4 +4206,4 @@ declare class TimeoutError extends ProofChainError {
4150
4206
  constructor(message?: string);
4151
4207
  }
4152
4208
 
4153
- export { type Achievement, type ActivitySummaryView, type AddNFTRequest, type AnchorResult, type ApiKey, type AttestRequest, type AttestationConfig, type AttestationMode, type Attestation as AttestationRecord, type AttestationResult, type AttestationSchema, AttestationsClient, AuthenticationError, AuthorizationError, type AvailableView, type Badge, type BatchIngestRequest, type BatchIngestResponse, type BatchVerifyResult, type BlockchainProof, type BlockchainStats, type Certificate, type CertificateVerifyResult, CertificatesResource, type Channel, type ChannelState, type ChannelStatus, ChannelsResource, type ClaimNFTResult, type ClaimRewardResult, type CohortDefinition, type CohortGroupStats, CohortLeaderboardClient, type CohortLeaderboardEntry, type CohortLeaderboardOptions, type CohortLeaderboardResponse, type ComprehensiveWalletInfo, type CreateAchievementRequest, type CreateApiKeyRequest, type CreateAttestationRequest, type CreateBadgeRequest, type CreateChannelRequest, type CreateCredentialTypeRequest, type CreateDataViewRequest, type CreateDualWalletsRequest, type CreateEndUserRequest, type CreateEventRequest, type CreatePassportDefinitionRequest, type CreatePassportRequest, type CreateQuestRequest, type CreateQuestStepRequest, type CreateRewardDefinitionRequest, type CreateSchemaRequest, type CreateTemplateFieldRequest, type CreateTemplateRequest, type CreateWalletRequest as CreateUserWalletRequest, type CreateWalletRequest$1 as CreateWalletRequest, type CreateWebhookRequest, type CredentialType, type CredentialVerifyResult, CredentialsClient, type DataViewColumn, type DataViewComputation, type DataViewDetail, type DataViewExecuteResult, type DataViewInfo, type DataViewListResponse, type DataViewPreviewRequest, type DataViewPreviewResult, type DataViewSummary, DataViewsClient, type DistributeResult, DocumentsResource, type DualWallets, type EarnedReward, type EndUser, EndUserIngestionClient, type EndUserIngestionClientOptions, type EndUserListResponse, EndUsersClient, type Event, type EventBatchProof, type EventMetadata, type EventStatus, EventsResource, type ExecuteSwapRequest, type Facet, type FacetsResponse, type FanProfileView, type FanpassGroupStats, FanpassLeaderboardClient, type FanpassLeaderboardEntry, type FanpassLeaderboardOptions, type FanpassLeaderboardResponse, type FanpassUserComparisonResponse, type FieldValue, type GDPRDeletionRequest, type GDPRDeletionResponse, type GDPRPreviewResponse, type IngestEventRequest, type IngestEventResponse, IngestionClient, type IngestionClientOptions, type IssueCertificateRequest, type IssueCredentialRequest, type IssuedCredential, type LeaderboardUserProfile$1 as LeaderboardUserProfile, type LinkWalletRequest, type ListAttestationsOptions, type ListCertificatesRequest, type ListCohortsOptions, type ListEndUsersOptions, type ListEventsRequest, type ListIssuedCredentialsOptions, type ListMintsOptions, type ListMintsResponse, type ListQuestsOptions, type ListRewardsOptions, type ListSchemasOptions, type ListWalletCredentialsOptions, type ManualRewardRequest, type MergeUsersRequest, type Milestone, type MintNFTRequest, type NFT, type NFTMint, NetworkError, NftClient, NotFoundError, type NotificationCallback, type NotificationEvent, type NotificationEventType, NotificationsClient, type OTTConfigResponse, type OTTConfigUpdate, type OTTRequestResponse, PartnerKeysClient, type Passport, PassportClient, type PassportDefinition, type PassportFieldValue, type PassportHistory, type PassportTemplate, type PassportV2Data, type PassportWithFields, ProofChain, ProofChainError, type ProofChainOptions, type ProofVerifyResult, type PushSubscriptionJSON, type Quest, type QuestStep, type QuestWithProgress, QuestsClient, RateLimitError, type RegisterWalletRequest, type RevokeResult, type RewardAsset, type RewardAttestationResult, type RewardDefinition, type RewardEarned, type VerifyResult as RewardVerifyResult, RewardsClient, type Schema, type SchemaDetail, type SchemaField, type SchemaListResponse, type ValidationError$1 as SchemaValidationError, SchemasClient, type SearchFilters, type SearchQueryRequest, type SearchRequest, SearchResource, type SearchResponse, type SearchResult, type SearchStats, ServerError, type SetProfileRequest, type Settlement, type StepCompletionResult, type StepProgress, type StepStartResult, type StreamAck, type StreamEventRequest, type SubscribeOptions, type SwapQuote, type SwapQuoteRequest, type SwapResult, type TemplateField, type TenantInfo, TenantResource, type TierDefinition, TimeoutError, type TokenBalance, type TransferRequest, type TransferResult, type Unsubscribe, type UpdateConfigRequest as UpdateAttestationConfigRequest, type UpdateDataViewRequest, type UpdateEndUserRequest, type UpdatePassportRequest, type UpdateWebhookRequest, type UsageStats, type UserAchievement, type UserActivity, type UserActivityResponse, type UserBadge, type UserBreakdownResponse, type UserCohortBreakdownEntry, type UserCredentialsSummary, type UserQuestProgress, type UserReward$1 as UserReward, type UserRewardsResponse, type UserWalletSummary, type UserWithWallets, type UsersWithWalletsResponse, type ValidateDataRequest, ValidationError, type ValidationErrorDetail, type ValidationResult, type VaultFile, type VaultFolder, type VaultListResponse, VaultResource, type VaultStats, type VaultUploadRequest, type VerificationResult, VerifyResource, type ViewColumn, type ViewTemplate, type Wallet, type WalletBalance, WalletClient, type WalletCreationResult, type WalletCredential, type WalletCredentialVerifySummary, type WalletStats, type Webhook, WebhooksResource };
4209
+ export { type Achievement, type ActivitySummaryView, type AddNFTRequest, type AnchorResult, type ApiKey, type AttestRequest, type AttestationConfig, type AttestationMode, type Attestation as AttestationRecord, type AttestationResult, type AttestationSchema, AttestationsClient, AuthenticationError, AuthorizationError, type AvailableView, type Badge, type BatchIngestRequest, type BatchIngestResponse, type BatchVerifyResult, type BlockchainProof, type BlockchainStats, type Certificate, type CertificateVerifyResult, CertificatesResource, type Channel, type ChannelState, type ChannelStatus, ChannelsResource, type ClaimNFTResult, type ClaimRewardResult, type CohortDefinition, type CohortGroupStats, CohortLeaderboardClient, type CohortLeaderboardEntry, type CohortLeaderboardOptions, type CohortLeaderboardResponse, type ComprehensiveWalletInfo, type CreateAchievementRequest, type CreateApiKeyRequest, type CreateAttestationRequest, type CreateBadgeRequest, type CreateChannelRequest, type CreateCredentialTypeRequest, type CreateDataViewRequest, type CreateDualWalletsRequest, type CreateEndUserRequest, type CreateEventRequest, type CreatePassportDefinitionRequest, type CreatePassportRequest, type CreateQuestRequest, type CreateQuestStepRequest, type CreateRewardDefinitionRequest, type CreateSchemaRequest, type CreateTemplateFieldRequest, type CreateTemplateRequest, type CreateWalletRequest as CreateUserWalletRequest, type CreateWalletRequest$1 as CreateWalletRequest, type CreateWebhookRequest, type CredentialType, type CredentialVerifyResult, CredentialsClient, type DataViewColumn, type DataViewComputation, type DataViewDetail, type DataViewExecuteResult, type DataViewInfo, type DataViewListResponse, type DataViewPreviewRequest, type DataViewPreviewResult, type DataViewSummary, DataViewsClient, type DistributeResult, DocumentsResource, type DualWallets, type EarnedReward, type EndUser, EndUserIngestionClient, type EndUserIngestionClientOptions, type EndUserListResponse, EndUsersClient, type Event, type EventBatchProof, type EventMetadata, type EventStatus, EventsResource, type ExecuteSwapRequest, type Facet, type FacetsResponse, type FanProfileView, type FanpassGroupStats, FanpassLeaderboardClient, type FanpassLeaderboardEntry, type FanpassLeaderboardOptions, type FanpassLeaderboardResponse, type FanpassUserComparisonResponse, type FieldValue, type GDPRDeletionRequest, type GDPRDeletionResponse, type GDPRPreviewResponse, type IngestEventRequest, type IngestEventResponse, IngestionClient, type IngestionClientOptions, type IssueCertificateRequest, type IssueCredentialRequest, type IssuedCredential, type LeaderboardUserProfile$1 as LeaderboardUserProfile, type LinkWalletRequest, type ListAttestationsOptions, type ListCertificatesRequest, type ListCohortsOptions, type ListEndUsersOptions, type ListEventsRequest, type ListIssuedCredentialsOptions, type ListMintsOptions, type ListMintsResponse, type ListQuestsOptions, type ListRewardsOptions, type ListSchemasOptions, type ListWalletCredentialsOptions, type ManualRewardRequest, type MergeUsersRequest, type Milestone, type MintNFTRequest, type NFT, type NFTMint, NetworkError, NftClient, NotFoundError, type NotificationCallback, type NotificationEvent, type NotificationEventType, NotificationsClient, type OTTConfigResponse, type OTTConfigUpdate, type OTTGenerateRequest, type OTTGenerateResponse, type OTTRedeemResponse, type OTTRequestResponse, PartnerKeysClient, type Passport, PassportClient, type PassportDefinition, type PassportFieldValue, type PassportHistory, type PassportTemplate, type PassportV2Data, type PassportWithFields, ProofChain, ProofChainError, type ProofChainOptions, type ProofVerifyResult, type PushSubscriptionJSON, type Quest, type QuestStep, type QuestWithProgress, QuestsClient, RateLimitError, type RegisterWalletRequest, type RevokeResult, type RewardAsset, type RewardAttestationResult, type RewardDefinition, type RewardEarned, type VerifyResult as RewardVerifyResult, RewardsClient, type Schema, type SchemaDetail, type SchemaField, type SchemaListResponse, type ValidationError$1 as SchemaValidationError, SchemasClient, type SearchFilters, type SearchQueryRequest, type SearchRequest, SearchResource, type SearchResponse, type SearchResult, type SearchStats, ServerError, type SetProfileRequest, type Settlement, type StepCompletionResult, type StepProgress, type StepStartResult, type StreamAck, type StreamEventRequest, type SubscribeOptions, type SwapQuote, type SwapQuoteRequest, type SwapResult, type TemplateField, type TenantInfo, TenantResource, type TierDefinition, TimeoutError, type TokenBalance, type TransferRequest, type TransferResult, type Unsubscribe, type UpdateConfigRequest as UpdateAttestationConfigRequest, type UpdateDataViewRequest, type UpdateEndUserRequest, type UpdatePassportRequest, type UpdateWebhookRequest, type UsageStats, type UserAchievement, type UserActivity, type UserActivityResponse, type UserBadge, type UserBreakdownResponse, type UserCohortBreakdownEntry, type UserCredentialsSummary, type UserQuestProgress, type UserReward$1 as UserReward, type UserRewardsResponse, type UserWalletSummary, type UserWithWallets, type UsersWithWalletsResponse, type ValidateDataRequest, ValidationError, type ValidationErrorDetail, type ValidationResult, type VaultFile, type VaultFolder, type VaultListResponse, VaultResource, type VaultStats, type VaultUploadRequest, type VerificationResult, VerifyResource, type ViewColumn, type ViewTemplate, type Wallet, type WalletBalance, WalletClient, type WalletCreationResult, type WalletCredential, type WalletCredentialVerifySummary, type WalletStats, type Webhook, WebhooksResource };
package/dist/index.js CHANGED
@@ -125,6 +125,9 @@ var DEFAULT_TIMEOUT = 3e4;
125
125
  var USER_AGENT = "proofchain-js/0.1.0";
126
126
  var HttpClient = class {
127
127
  constructor(options) {
128
+ // In-flight GET de-duplication: concurrent identical GETs share one network
129
+ // request (and its response), cutting origin load for chatty UIs/pollers.
130
+ this.inflight = /* @__PURE__ */ new Map();
128
131
  this.apiKey = options.apiKey || "";
129
132
  this.userToken = options.userToken || "";
130
133
  this.tenantId = options.tenantId || "";
@@ -137,13 +140,14 @@ var HttpClient = class {
137
140
  "Content-Type": "application/json",
138
141
  "User-Agent": USER_AGENT
139
142
  };
143
+ if (this.apiKey) {
144
+ headers["X-API-Key"] = this.apiKey;
145
+ }
140
146
  if (this.userToken) {
141
147
  headers["Authorization"] = `Bearer ${this.userToken}`;
142
148
  if (this.tenantId) {
143
149
  headers["X-Tenant-ID"] = this.tenantId;
144
150
  }
145
- } else if (this.apiKey) {
146
- headers["X-API-Key"] = this.apiKey;
147
151
  }
148
152
  return headers;
149
153
  }
@@ -192,7 +196,8 @@ var HttpClient = class {
192
196
  clearTimeout(timeoutId);
193
197
  if (error instanceof ProofChainError) {
194
198
  if (error instanceof RateLimitError && retries < this.maxRetries) {
195
- const delay = Math.min((error.retryAfter || 1) * 1e3, 6e4);
199
+ const base = Math.min((error.retryAfter || 1) * 1e3, 6e4);
200
+ const delay = base + Math.floor(Math.random() * 1e3);
196
201
  await new Promise((resolve) => setTimeout(resolve, delay));
197
202
  return this.fetchWithRetry(url, options, retries + 1);
198
203
  }
@@ -203,7 +208,9 @@ var HttpClient = class {
203
208
  throw new TimeoutError();
204
209
  }
205
210
  if (retries < this.maxRetries) {
206
- await new Promise((resolve) => setTimeout(resolve, 1e3 * (retries + 1)));
211
+ const base = 1e3 * (retries + 1);
212
+ const delay = base + Math.floor(Math.random() * 500);
213
+ await new Promise((resolve) => setTimeout(resolve, delay));
207
214
  return this.fetchWithRetry(url, options, retries + 1);
208
215
  }
209
216
  throw new NetworkError(error.message, error);
@@ -232,6 +239,17 @@ var HttpClient = class {
232
239
  if (options.body) {
233
240
  fetchOptions.body = JSON.stringify(options.body);
234
241
  }
242
+ if (method === "GET") {
243
+ const existing = this.inflight.get(url);
244
+ if (existing) {
245
+ return existing;
246
+ }
247
+ const pending = this.fetchWithRetry(url, fetchOptions).finally(() => {
248
+ this.inflight.delete(url);
249
+ });
250
+ this.inflight.set(url, pending);
251
+ return pending;
252
+ }
235
253
  return this.fetchWithRetry(url, fetchOptions);
236
254
  }
237
255
  async requestMultipart(path, formData) {
@@ -239,13 +257,14 @@ var HttpClient = class {
239
257
  const headers = {
240
258
  "User-Agent": USER_AGENT
241
259
  };
260
+ if (this.apiKey) {
261
+ headers["X-API-Key"] = this.apiKey;
262
+ }
242
263
  if (this.userToken) {
243
264
  headers["Authorization"] = `Bearer ${this.userToken}`;
244
265
  if (this.tenantId) {
245
266
  headers["X-Tenant-ID"] = this.tenantId;
246
267
  }
247
- } else if (this.apiKey) {
248
- headers["X-API-Key"] = this.apiKey;
249
268
  }
250
269
  return this.fetchWithRetry(url, {
251
270
  method: "POST",
@@ -1724,11 +1743,10 @@ var QuestsClient = class {
1724
1743
  return this.http.get(`/quests/available?${params.toString()}`);
1725
1744
  }
1726
1745
  /**
1727
- * Get a quest by slug
1746
+ * REMOVED: getBySlug() the server never implemented GET /quests/slug/{slug}.
1747
+ * There is no slug-based lookup route in the API. Use list() with a status/category
1748
+ * filter and find the quest client-side, or use get() with the quest ID.
1728
1749
  */
1729
- async getBySlug(slug) {
1730
- return this.http.get(`/quests/slug/${encodeURIComponent(slug)}`);
1731
- }
1732
1750
  /**
1733
1751
  * Create a quest
1734
1752
  */
@@ -1769,11 +1787,9 @@ var QuestsClient = class {
1769
1787
  return this.http.post(`/quests/${questId}/pause`, {});
1770
1788
  }
1771
1789
  /**
1772
- * Archive a quest
1790
+ * REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
1791
+ * To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
1773
1792
  */
1774
- async archive(questId) {
1775
- return this.http.post(`/quests/${questId}/archive`, {});
1776
- }
1777
1793
  // ---------------------------------------------------------------------------
1778
1794
  // User Progress
1779
1795
  // ---------------------------------------------------------------------------
@@ -1853,28 +1869,144 @@ var QuestsClient = class {
1853
1869
  // Quest Steps
1854
1870
  // ---------------------------------------------------------------------------
1855
1871
  /**
1856
- * Add a step to a quest
1872
+ * Add a step to a quest.
1873
+ *
1874
+ * The server has no dedicated step-create endpoint — steps are managed via the
1875
+ * full-replace PUT /quests/{id}. This method fetches the quest, appends the new
1876
+ * step, and issues a full update. The returned QuestStep is synthesised from the
1877
+ * updated quest's steps array (matched by order or last position).
1857
1878
  */
1858
1879
  async addStep(questId, step) {
1859
- return this.http.post(`/quests/${questId}/steps`, step);
1860
- }
1861
- /**
1862
- * Update a step
1880
+ const quest = await this.get(questId);
1881
+ const newOrder = step.order ?? (quest.steps.length > 0 ? Math.max(...quest.steps.map((s) => s.order)) + 1 : 0);
1882
+ const stepsPayload = [
1883
+ ...quest.steps.map((s) => ({
1884
+ name: s.name,
1885
+ description: s.description,
1886
+ order: s.order,
1887
+ step_type: s.step_type,
1888
+ event_type: s.event_type,
1889
+ event_types: s.event_types,
1890
+ criteria: s.criteria,
1891
+ required_data_fields: s.required_data_fields,
1892
+ step_points: s.step_points,
1893
+ cta_text: s.cta_text,
1894
+ cta_url: s.cta_url,
1895
+ icon_url: s.icon_url,
1896
+ is_optional: s.is_optional
1897
+ })),
1898
+ { ...step, order: newOrder }
1899
+ ];
1900
+ const updated = await this.update(questId, { steps: stepsPayload });
1901
+ const added = updated.steps.find((s) => s.order === newOrder) ?? updated.steps[updated.steps.length - 1];
1902
+ return added;
1903
+ }
1904
+ /**
1905
+ * Update a step by step ID.
1906
+ *
1907
+ * The server has no dedicated step-update endpoint — steps are managed via the
1908
+ * full-replace PUT /quests/{id}. This method fetches the quest, replaces the
1909
+ * matching step, and issues a full update.
1863
1910
  */
1864
1911
  async updateStep(questId, stepId, data) {
1865
- return this.http.put(`/quests/${questId}/steps/${stepId}`, data);
1912
+ const quest = await this.get(questId);
1913
+ const stepsPayload = quest.steps.map((s) => {
1914
+ if (s.id !== stepId) {
1915
+ return {
1916
+ name: s.name,
1917
+ description: s.description,
1918
+ order: s.order,
1919
+ step_type: s.step_type,
1920
+ event_type: s.event_type,
1921
+ event_types: s.event_types,
1922
+ criteria: s.criteria,
1923
+ required_data_fields: s.required_data_fields,
1924
+ step_points: s.step_points,
1925
+ cta_text: s.cta_text,
1926
+ cta_url: s.cta_url,
1927
+ icon_url: s.icon_url,
1928
+ is_optional: s.is_optional
1929
+ };
1930
+ }
1931
+ return {
1932
+ name: s.name,
1933
+ description: s.description,
1934
+ order: s.order,
1935
+ step_type: s.step_type,
1936
+ event_type: s.event_type,
1937
+ event_types: s.event_types,
1938
+ criteria: s.criteria,
1939
+ required_data_fields: s.required_data_fields,
1940
+ step_points: s.step_points,
1941
+ cta_text: s.cta_text,
1942
+ cta_url: s.cta_url,
1943
+ icon_url: s.icon_url,
1944
+ is_optional: s.is_optional,
1945
+ ...data
1946
+ };
1947
+ });
1948
+ const updated = await this.update(questId, { steps: stepsPayload });
1949
+ const found = updated.steps.find((s) => s.id === stepId);
1950
+ if (!found) throw new Error(`Step ${stepId} not found after update`);
1951
+ return found;
1866
1952
  }
1867
1953
  /**
1868
- * Delete a step
1954
+ * Delete a step by step ID.
1955
+ *
1956
+ * The server has no dedicated step-delete endpoint — steps are managed via the
1957
+ * full-replace PUT /quests/{id}. This method fetches the quest, filters out the
1958
+ * target step, and issues a full update.
1869
1959
  */
1870
1960
  async deleteStep(questId, stepId) {
1871
- await this.http.delete(`/quests/${questId}/steps/${stepId}`);
1872
- }
1873
- /**
1874
- * Reorder steps
1961
+ const quest = await this.get(questId);
1962
+ const stepsPayload = quest.steps.filter((s) => s.id !== stepId).map((s) => ({
1963
+ name: s.name,
1964
+ description: s.description,
1965
+ order: s.order,
1966
+ step_type: s.step_type,
1967
+ event_type: s.event_type,
1968
+ event_types: s.event_types,
1969
+ criteria: s.criteria,
1970
+ required_data_fields: s.required_data_fields,
1971
+ step_points: s.step_points,
1972
+ cta_text: s.cta_text,
1973
+ cta_url: s.cta_url,
1974
+ icon_url: s.icon_url,
1975
+ is_optional: s.is_optional
1976
+ }));
1977
+ await this.update(questId, { steps: stepsPayload });
1978
+ }
1979
+ /**
1980
+ * Reorder steps by providing step IDs in the desired order.
1981
+ *
1982
+ * The server has no dedicated reorder endpoint — steps are managed via the
1983
+ * full-replace PUT /quests/{id}. This method fetches the quest, reassigns
1984
+ * the `order` field according to the given stepIds sequence, and issues a
1985
+ * full update.
1875
1986
  */
1876
1987
  async reorderSteps(questId, stepIds) {
1877
- return this.http.post(`/quests/${questId}/steps/reorder`, { step_ids: stepIds });
1988
+ const quest = await this.get(questId);
1989
+ const stepMap = new Map(quest.steps.map((s) => [s.id, s]));
1990
+ const stepsPayload = stepIds.map((id, index) => {
1991
+ const s = stepMap.get(id);
1992
+ if (!s) throw new Error(`Step ${id} not found in quest ${questId}`);
1993
+ return {
1994
+ name: s.name,
1995
+ description: s.description,
1996
+ order: index,
1997
+ step_type: s.step_type,
1998
+ event_type: s.event_type,
1999
+ event_types: s.event_types,
2000
+ criteria: s.criteria,
2001
+ required_data_fields: s.required_data_fields,
2002
+ step_points: s.step_points,
2003
+ cta_text: s.cta_text,
2004
+ cta_url: s.cta_url,
2005
+ icon_url: s.icon_url,
2006
+ is_optional: s.is_optional
2007
+ };
2008
+ });
2009
+ return this.update(questId, { steps: stepsPayload });
1878
2010
  }
1879
2011
  };
1880
2012
 
@@ -2711,23 +2843,44 @@ var PartnerKeysClient = class {
2711
2843
  this.http = http;
2712
2844
  }
2713
2845
  /**
2714
- * Request a one-time token for a partner key (end-user JWKS auth).
2846
+ * Generate a one-time token for the authenticated end-user.
2847
+ *
2848
+ * Requires: tenant API key (X-API-Key) + end-user JWT (Authorization: Bearer).
2849
+ * The SDK must be initialised with both `apiKey` and `userToken`.
2850
+ *
2851
+ * @param options.ttl - Token lifetime in seconds (30–3600, default 300)
2852
+ * @param options.session_data_keys - Whitelist of JWT claims to include
2715
2853
  */
2716
- async requestOTT(keyId) {
2717
- return this.http.post(`/partner-keys/${keyId}/ott/request`);
2854
+ async generate(options) {
2855
+ return this.http.post("/partner-keys/generate", options ?? {});
2718
2856
  }
2719
2857
  /**
2720
- * Update OTT configuration for a partner key.
2858
+ * Redeem a one-time token to get the user's identity.
2859
+ *
2860
+ * Requires: integrator API key (X-API-Key). The token is single-use
2861
+ * and deleted on successful redemption.
2862
+ *
2863
+ * @param ott - The opaque one-time token
2864
+ */
2865
+ async redeem(ott) {
2866
+ return this.http.post("/partner-keys/redeem", { ott });
2867
+ }
2868
+ /**
2869
+ * Update OTT configuration for a partner key (tenant admin).
2721
2870
  */
2722
2871
  async updateOTTConfig(keyId, config) {
2723
2872
  return this.http.patch(`/partner-keys/${keyId}/ott-config`, config);
2724
2873
  }
2725
2874
  /**
2726
- * Get OTT configuration for a partner key.
2875
+ * Get OTT configuration for a partner key (tenant admin).
2727
2876
  */
2728
2877
  async getOTTConfig(keyId) {
2729
2878
  return this.http.get(`/partner-keys/${keyId}/ott-config`);
2730
2879
  }
2880
+ /** @deprecated Use generate() */
2881
+ async requestOTT(_keyId) {
2882
+ return this.generate();
2883
+ }
2731
2884
  };
2732
2885
 
2733
2886
  // src/attestations.ts
package/dist/index.mjs CHANGED
@@ -65,6 +65,9 @@ var DEFAULT_TIMEOUT = 3e4;
65
65
  var USER_AGENT = "proofchain-js/0.1.0";
66
66
  var HttpClient = class {
67
67
  constructor(options) {
68
+ // In-flight GET de-duplication: concurrent identical GETs share one network
69
+ // request (and its response), cutting origin load for chatty UIs/pollers.
70
+ this.inflight = /* @__PURE__ */ new Map();
68
71
  this.apiKey = options.apiKey || "";
69
72
  this.userToken = options.userToken || "";
70
73
  this.tenantId = options.tenantId || "";
@@ -77,13 +80,14 @@ var HttpClient = class {
77
80
  "Content-Type": "application/json",
78
81
  "User-Agent": USER_AGENT
79
82
  };
83
+ if (this.apiKey) {
84
+ headers["X-API-Key"] = this.apiKey;
85
+ }
80
86
  if (this.userToken) {
81
87
  headers["Authorization"] = `Bearer ${this.userToken}`;
82
88
  if (this.tenantId) {
83
89
  headers["X-Tenant-ID"] = this.tenantId;
84
90
  }
85
- } else if (this.apiKey) {
86
- headers["X-API-Key"] = this.apiKey;
87
91
  }
88
92
  return headers;
89
93
  }
@@ -132,7 +136,8 @@ var HttpClient = class {
132
136
  clearTimeout(timeoutId);
133
137
  if (error instanceof ProofChainError) {
134
138
  if (error instanceof RateLimitError && retries < this.maxRetries) {
135
- const delay = Math.min((error.retryAfter || 1) * 1e3, 6e4);
139
+ const base = Math.min((error.retryAfter || 1) * 1e3, 6e4);
140
+ const delay = base + Math.floor(Math.random() * 1e3);
136
141
  await new Promise((resolve) => setTimeout(resolve, delay));
137
142
  return this.fetchWithRetry(url, options, retries + 1);
138
143
  }
@@ -143,7 +148,9 @@ var HttpClient = class {
143
148
  throw new TimeoutError();
144
149
  }
145
150
  if (retries < this.maxRetries) {
146
- await new Promise((resolve) => setTimeout(resolve, 1e3 * (retries + 1)));
151
+ const base = 1e3 * (retries + 1);
152
+ const delay = base + Math.floor(Math.random() * 500);
153
+ await new Promise((resolve) => setTimeout(resolve, delay));
147
154
  return this.fetchWithRetry(url, options, retries + 1);
148
155
  }
149
156
  throw new NetworkError(error.message, error);
@@ -172,6 +179,17 @@ var HttpClient = class {
172
179
  if (options.body) {
173
180
  fetchOptions.body = JSON.stringify(options.body);
174
181
  }
182
+ if (method === "GET") {
183
+ const existing = this.inflight.get(url);
184
+ if (existing) {
185
+ return existing;
186
+ }
187
+ const pending = this.fetchWithRetry(url, fetchOptions).finally(() => {
188
+ this.inflight.delete(url);
189
+ });
190
+ this.inflight.set(url, pending);
191
+ return pending;
192
+ }
175
193
  return this.fetchWithRetry(url, fetchOptions);
176
194
  }
177
195
  async requestMultipart(path, formData) {
@@ -179,13 +197,14 @@ var HttpClient = class {
179
197
  const headers = {
180
198
  "User-Agent": USER_AGENT
181
199
  };
200
+ if (this.apiKey) {
201
+ headers["X-API-Key"] = this.apiKey;
202
+ }
182
203
  if (this.userToken) {
183
204
  headers["Authorization"] = `Bearer ${this.userToken}`;
184
205
  if (this.tenantId) {
185
206
  headers["X-Tenant-ID"] = this.tenantId;
186
207
  }
187
- } else if (this.apiKey) {
188
- headers["X-API-Key"] = this.apiKey;
189
208
  }
190
209
  return this.fetchWithRetry(url, {
191
210
  method: "POST",
@@ -1664,11 +1683,10 @@ var QuestsClient = class {
1664
1683
  return this.http.get(`/quests/available?${params.toString()}`);
1665
1684
  }
1666
1685
  /**
1667
- * Get a quest by slug
1686
+ * REMOVED: getBySlug() the server never implemented GET /quests/slug/{slug}.
1687
+ * There is no slug-based lookup route in the API. Use list() with a status/category
1688
+ * filter and find the quest client-side, or use get() with the quest ID.
1668
1689
  */
1669
- async getBySlug(slug) {
1670
- return this.http.get(`/quests/slug/${encodeURIComponent(slug)}`);
1671
- }
1672
1690
  /**
1673
1691
  * Create a quest
1674
1692
  */
@@ -1709,11 +1727,9 @@ var QuestsClient = class {
1709
1727
  return this.http.post(`/quests/${questId}/pause`, {});
1710
1728
  }
1711
1729
  /**
1712
- * Archive a quest
1730
+ * REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
1731
+ * To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
1713
1732
  */
1714
- async archive(questId) {
1715
- return this.http.post(`/quests/${questId}/archive`, {});
1716
- }
1717
1733
  // ---------------------------------------------------------------------------
1718
1734
  // User Progress
1719
1735
  // ---------------------------------------------------------------------------
@@ -1793,28 +1809,144 @@ var QuestsClient = class {
1793
1809
  // Quest Steps
1794
1810
  // ---------------------------------------------------------------------------
1795
1811
  /**
1796
- * Add a step to a quest
1812
+ * Add a step to a quest.
1813
+ *
1814
+ * The server has no dedicated step-create endpoint — steps are managed via the
1815
+ * full-replace PUT /quests/{id}. This method fetches the quest, appends the new
1816
+ * step, and issues a full update. The returned QuestStep is synthesised from the
1817
+ * updated quest's steps array (matched by order or last position).
1797
1818
  */
1798
1819
  async addStep(questId, step) {
1799
- return this.http.post(`/quests/${questId}/steps`, step);
1800
- }
1801
- /**
1802
- * Update a step
1820
+ const quest = await this.get(questId);
1821
+ const newOrder = step.order ?? (quest.steps.length > 0 ? Math.max(...quest.steps.map((s) => s.order)) + 1 : 0);
1822
+ const stepsPayload = [
1823
+ ...quest.steps.map((s) => ({
1824
+ name: s.name,
1825
+ description: s.description,
1826
+ order: s.order,
1827
+ step_type: s.step_type,
1828
+ event_type: s.event_type,
1829
+ event_types: s.event_types,
1830
+ criteria: s.criteria,
1831
+ required_data_fields: s.required_data_fields,
1832
+ step_points: s.step_points,
1833
+ cta_text: s.cta_text,
1834
+ cta_url: s.cta_url,
1835
+ icon_url: s.icon_url,
1836
+ is_optional: s.is_optional
1837
+ })),
1838
+ { ...step, order: newOrder }
1839
+ ];
1840
+ const updated = await this.update(questId, { steps: stepsPayload });
1841
+ const added = updated.steps.find((s) => s.order === newOrder) ?? updated.steps[updated.steps.length - 1];
1842
+ return added;
1843
+ }
1844
+ /**
1845
+ * Update a step by step ID.
1846
+ *
1847
+ * The server has no dedicated step-update endpoint — steps are managed via the
1848
+ * full-replace PUT /quests/{id}. This method fetches the quest, replaces the
1849
+ * matching step, and issues a full update.
1803
1850
  */
1804
1851
  async updateStep(questId, stepId, data) {
1805
- return this.http.put(`/quests/${questId}/steps/${stepId}`, data);
1852
+ const quest = await this.get(questId);
1853
+ const stepsPayload = quest.steps.map((s) => {
1854
+ if (s.id !== stepId) {
1855
+ return {
1856
+ name: s.name,
1857
+ description: s.description,
1858
+ order: s.order,
1859
+ step_type: s.step_type,
1860
+ event_type: s.event_type,
1861
+ event_types: s.event_types,
1862
+ criteria: s.criteria,
1863
+ required_data_fields: s.required_data_fields,
1864
+ step_points: s.step_points,
1865
+ cta_text: s.cta_text,
1866
+ cta_url: s.cta_url,
1867
+ icon_url: s.icon_url,
1868
+ is_optional: s.is_optional
1869
+ };
1870
+ }
1871
+ return {
1872
+ name: s.name,
1873
+ description: s.description,
1874
+ order: s.order,
1875
+ step_type: s.step_type,
1876
+ event_type: s.event_type,
1877
+ event_types: s.event_types,
1878
+ criteria: s.criteria,
1879
+ required_data_fields: s.required_data_fields,
1880
+ step_points: s.step_points,
1881
+ cta_text: s.cta_text,
1882
+ cta_url: s.cta_url,
1883
+ icon_url: s.icon_url,
1884
+ is_optional: s.is_optional,
1885
+ ...data
1886
+ };
1887
+ });
1888
+ const updated = await this.update(questId, { steps: stepsPayload });
1889
+ const found = updated.steps.find((s) => s.id === stepId);
1890
+ if (!found) throw new Error(`Step ${stepId} not found after update`);
1891
+ return found;
1806
1892
  }
1807
1893
  /**
1808
- * Delete a step
1894
+ * Delete a step by step ID.
1895
+ *
1896
+ * The server has no dedicated step-delete endpoint — steps are managed via the
1897
+ * full-replace PUT /quests/{id}. This method fetches the quest, filters out the
1898
+ * target step, and issues a full update.
1809
1899
  */
1810
1900
  async deleteStep(questId, stepId) {
1811
- await this.http.delete(`/quests/${questId}/steps/${stepId}`);
1812
- }
1813
- /**
1814
- * Reorder steps
1901
+ const quest = await this.get(questId);
1902
+ const stepsPayload = quest.steps.filter((s) => s.id !== stepId).map((s) => ({
1903
+ name: s.name,
1904
+ description: s.description,
1905
+ order: s.order,
1906
+ step_type: s.step_type,
1907
+ event_type: s.event_type,
1908
+ event_types: s.event_types,
1909
+ criteria: s.criteria,
1910
+ required_data_fields: s.required_data_fields,
1911
+ step_points: s.step_points,
1912
+ cta_text: s.cta_text,
1913
+ cta_url: s.cta_url,
1914
+ icon_url: s.icon_url,
1915
+ is_optional: s.is_optional
1916
+ }));
1917
+ await this.update(questId, { steps: stepsPayload });
1918
+ }
1919
+ /**
1920
+ * Reorder steps by providing step IDs in the desired order.
1921
+ *
1922
+ * The server has no dedicated reorder endpoint — steps are managed via the
1923
+ * full-replace PUT /quests/{id}. This method fetches the quest, reassigns
1924
+ * the `order` field according to the given stepIds sequence, and issues a
1925
+ * full update.
1815
1926
  */
1816
1927
  async reorderSteps(questId, stepIds) {
1817
- return this.http.post(`/quests/${questId}/steps/reorder`, { step_ids: stepIds });
1928
+ const quest = await this.get(questId);
1929
+ const stepMap = new Map(quest.steps.map((s) => [s.id, s]));
1930
+ const stepsPayload = stepIds.map((id, index) => {
1931
+ const s = stepMap.get(id);
1932
+ if (!s) throw new Error(`Step ${id} not found in quest ${questId}`);
1933
+ return {
1934
+ name: s.name,
1935
+ description: s.description,
1936
+ order: index,
1937
+ step_type: s.step_type,
1938
+ event_type: s.event_type,
1939
+ event_types: s.event_types,
1940
+ criteria: s.criteria,
1941
+ required_data_fields: s.required_data_fields,
1942
+ step_points: s.step_points,
1943
+ cta_text: s.cta_text,
1944
+ cta_url: s.cta_url,
1945
+ icon_url: s.icon_url,
1946
+ is_optional: s.is_optional
1947
+ };
1948
+ });
1949
+ return this.update(questId, { steps: stepsPayload });
1818
1950
  }
1819
1951
  };
1820
1952
 
@@ -2651,23 +2783,44 @@ var PartnerKeysClient = class {
2651
2783
  this.http = http;
2652
2784
  }
2653
2785
  /**
2654
- * Request a one-time token for a partner key (end-user JWKS auth).
2786
+ * Generate a one-time token for the authenticated end-user.
2787
+ *
2788
+ * Requires: tenant API key (X-API-Key) + end-user JWT (Authorization: Bearer).
2789
+ * The SDK must be initialised with both `apiKey` and `userToken`.
2790
+ *
2791
+ * @param options.ttl - Token lifetime in seconds (30–3600, default 300)
2792
+ * @param options.session_data_keys - Whitelist of JWT claims to include
2655
2793
  */
2656
- async requestOTT(keyId) {
2657
- return this.http.post(`/partner-keys/${keyId}/ott/request`);
2794
+ async generate(options) {
2795
+ return this.http.post("/partner-keys/generate", options ?? {});
2658
2796
  }
2659
2797
  /**
2660
- * Update OTT configuration for a partner key.
2798
+ * Redeem a one-time token to get the user's identity.
2799
+ *
2800
+ * Requires: integrator API key (X-API-Key). The token is single-use
2801
+ * and deleted on successful redemption.
2802
+ *
2803
+ * @param ott - The opaque one-time token
2804
+ */
2805
+ async redeem(ott) {
2806
+ return this.http.post("/partner-keys/redeem", { ott });
2807
+ }
2808
+ /**
2809
+ * Update OTT configuration for a partner key (tenant admin).
2661
2810
  */
2662
2811
  async updateOTTConfig(keyId, config) {
2663
2812
  return this.http.patch(`/partner-keys/${keyId}/ott-config`, config);
2664
2813
  }
2665
2814
  /**
2666
- * Get OTT configuration for a partner key.
2815
+ * Get OTT configuration for a partner key (tenant admin).
2667
2816
  */
2668
2817
  async getOTTConfig(keyId) {
2669
2818
  return this.http.get(`/partner-keys/${keyId}/ott-config`);
2670
2819
  }
2820
+ /** @deprecated Use generate() */
2821
+ async requestOTT(_keyId) {
2822
+ return this.generate();
2823
+ }
2671
2824
  };
2672
2825
 
2673
2826
  // src/attestations.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofchain/sdk",
3
- "version": "2.22.0",
3
+ "version": "3.0.0",
4
4
  "description": "Official JavaScript/TypeScript SDK for ProofChain - blockchain-anchored document attestation",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",