> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encorekit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# revokeEntitlements()

> Admin/testing method to expire all entitlements while preserving user identity

Expires all entitlements for the currently identified user while preserving their user ID and attributes. This is a convenience method designed for testing and development environments to simulate entitlement expiry and test offer flows repeatedly for the same user.

<Info>
  Unlike [`reset()`](./reset) which clears everything, `revokeEntitlements()` only expires entitlements - the user remains identified with their attributes intact. This allows you to test multiple offer cycles for the same user.
</Info>

## Signature

```swift theme={null}
public func revokeEntitlements() async throws
```

## Returns

No return value. Completes normally on success and throws an [`EncoreError`](./errors) on failure.

## Usage

### Complete Testing Lifecycle for a User

Test the full entitlement lifecycle from offer presentation through expiry and re-testing for the same user:

```swift theme={null}
func testCompleteEntitlementLifecycle() async {
  // User identity is preserved throughout this entire flow
  Encore.shared.identify(userId: "test_user_123")
  
  // 1. Verify no active entitlements at start
  let hasTrialInitially = await Encore.shared.isActive(.freeTrial())
  print("Has trial initially: \(hasTrialInitially)") // false
  
  // 2. Present offer and grant entitlement
  do {
    let result = try await Encore.shared.placement("test_flow").show()
    switch result {
    case .granted(let entitlement):
      print("✅ User accepted offer: \(entitlement)")
      
      // 3. Verify entitlement is now active
      let hasTrialAfterGrant = await Encore.shared.isActive(.freeTrial())
      print("Has trial after grant: \(hasTrialAfterGrant)") // true
      
    case .notGranted(let reason):
      print("User declined: \(reason)")
      return
    }
  } catch {
    print("Error presenting offer: \(error)")
    return
  }
  
  // 4. Simulate entitlement expiry using revokeEntitlements
  //    Note: User identity (test_user_123) is preserved
  do {
    try await Encore.shared.revokeEntitlements()
    print("✅ Entitlements expired - user still identified as test_user_123")
    
    // 5. Verify entitlement is no longer active
    let hasTrialAfterRevoke = await Encore.shared.isActive(.freeTrial())
    print("Has trial after revoke: \(hasTrialAfterRevoke)") // false
    
    // Ready to test another offer cycle for the same user
  } catch {
    print("❌ Failed to revoke: \(error)")
  }
}
```

## End-to-end sandbox test recipe

Use the **Test Key** together with `revokeEntitlements()` to run repeatable offer cycles for the same user from any location, and to watch the [two-phase entitlement flow](./entitlement-scope#two-phase-entitlements-provisional-vs-verified) (provisional → verified → revoked).

<Steps>
  <Step title="Configure with your Test Key">
    The Test Key (`pk_test_…`) bypasses geo-filtering so location-restricted offers appear anywhere.

    ```swift theme={null}
    #if DEBUG
    Encore.shared.configure(
        apiKey: "pk_test_<your_key>",
        options: .init(logLevel: .debug)   // verbose logs to follow the flow
    )
    Encore.shared.identify(userId: "test_user_456")
    #endif
    ```
  </Step>

  <Step title="Point App Store Connect at the sandbox webhook">
    In App Store Connect, set the **Sandbox Server URL** for App Store Server Notifications to Encore's endpoint so sandbox transactions reach the backend and can be verified:

    ```
    https://api.encorekit.com/encore/webhooks/apple
    ```

    See [App Store Server Notifications](/publishers/ios/platform-setup/server-notifications) for the full walkthrough.
  </Step>

  <Step title="Trigger an offer and observe provisional → verified">
    Present an offer and complete the sandbox purchase. With `unlock: .optimistic` (the default), the entitlement is granted **provisionally** the moment the user returns, then flips to **verified** once the backend confirms the transaction.

    ```swift theme={null}
    let result = try await Encore.shared.placement("test_offer").show()
    if case .granted = result {
        // Provisional grant — visible in the .all scope immediately.
        print("all:", await Encore.shared.isActive(.freeTrial(), in: .all))        // true (provisional)
        // Verified grant lands shortly after, once the webhook is processed.
        print("verified:", await Encore.shared.isActive(.freeTrial(), in: .verified)) // becomes true after verification
    }
    ```
  </Step>

  <Step title="Revoke and repeat">
    Expire all entitlements while keeping the user identified, then run another cycle for the same `test_user_456`:

    ```swift theme={null}
    try await Encore.shared.revokeEntitlements()
    print("all after revoke:", await Encore.shared.isActive(.freeTrial(), in: .all)) // false

    // Present again to the same user.
    let next = try await Encore.shared.placement("test_offer_2").show()
    ```
  </Step>
</Steps>

<Tip>
  The combination of the **Test Key** ([`configure()`](./configure#using-test-key-for-development)) and `revokeEntitlements()` lets you replay the full offer lifecycle for one user from any location, with no geo-filtering.
</Tip>

## Related Methods

* [`configure()`](./configure) - Configure SDK with admin API key
* [`isActive()`](./is-active) - Check entitlement status after revoking
* [`reset()`](./reset) - Clear user session and entitlements
* [`identify()`](./identify) - Set user ID for testing specific users
