> ## Documentation Index
> Fetch the complete documentation index at: https://www.1password.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Approve in your iOS app

> Present an access request to 1Password from your iOS app through the system share sheet, and get the person's grant back without leaving your app.

Partner preview

Your iOS app can present an access request to 1Password without sending the person to another app. You pass 1Password the access request reference you got when you created the request. 1Password shows the person a sheet that names your app and what it asked for, and they decide whether to grant it. Your app then gets back either the access grant or an error.

Your app talks to 1Password through its action extension. You present the request in the system share sheet, `UIActivityViewController`, and the person picks 1Password.

> [!NOTE]
>
> The action extension is available in 1Password for iOS 8.12.42 or later. See [Supported builds](/agentic-autofill/partners/supported-builds.md).

## How it works

1. Your app wraps the access request reference in an `OnePasswordAccessRequest`, encodes it as JSON, and attaches it to an `NSExtensionItem` as `com.1password.access-request`.
2. Your app presents a `UIActivityViewController` with that item, and the person picks 1Password.
3. 1Password shows the access request, unlocking first if needed, and the person grants or declines it.
4. Your app's `completionWithItemsHandler` receives either a `com.1password.access-grant` attachment, which decodes to an `OnePasswordAccessGrant`, or an error.

The grant comes back to your app directly, so you don't have to poll for the decision.

## Get the access request reference

Create the access request first. The reference is the value 1Password reads to find the account and the request, including what you asked for and why. It's the `access_request_reference` value in the `appLink` returned when you create the request, by the browser extension's `createAccessRequest` or by the SDK's [`accessRequests.create`](/agentic-autofill/partners/sdk#create-an-access-request). If you create requests with the SDK, contact 1Password first.

Pass the reference unchanged. Don't decode it, edit it, or build it from parts. Like the approval link, it carries your request text, so keep it out of logs and model context.

## Add the integration types

1Password provides these Swift definitions. Add them to your app target:

```swift theme={null}
import Foundation
import UniformTypeIdentifiers

public extension UTType {
    /// JSON access request accepted by the 1Password action extension.
    static let onePasswordAccessRequest = UTType(
        importedAs: "com.1password.access-request",
        conformingTo: .json
    )

    /// JSON access grant returned by the 1Password action extension.
    static let onePasswordAccessGrant = UTType(
        importedAs: "com.1password.access-grant",
        conformingTo: .json
    )
}

/// The access request your app sends to 1Password.
public struct OnePasswordAccessRequest: Codable {
    /// The protocol version this definition encodes.
    public static let currentVersion = 1

    /// The version of this request's structure.
    public let version: Int

    /// The access request reference your app was given when it created the access request.
    public let accessRequestReference: String

    public init(accessRequestReference: String) {
        version = Self.currentVersion
        self.accessRequestReference = accessRequestReference
    }
}

/// The access granted by 1Password: the access request's status, read after the person granted it.
public struct OnePasswordAccessGrant: Codable {
    public let path: String
    public let state: State
    public let resolved: [ResolvedEntry]

    public enum State: String, Codable {
        case pending
        case resolved
        case denied
        case failed
        case expired
    }

    public struct ResolvedEntry: Codable {
        public let entryId: String?
        public let reference: CredentialReference
    }

    public struct CredentialReference: Codable {
        public let reference: String
    }
}

/// Errors returned by the 1Password action extension.
public enum OnePasswordAccessRequestError: Error {
    case requestDecodeFailure
    case missingRequest
    case unsupportedVersion
    case declined
    case internalError
}
```

### The request

| Field | Type | Description |
| - | - | - |
| `version` | `Int` | Always `1` for this version of the protocol. `init(accessRequestReference:)` sets it for you. It covers only the structure of this request. The access request reference is versioned separately. |
| `accessRequestReference` | `String` | The access request reference you got when you created the access request. |

### The access grant

The access grant is the access request's status, read from 1Password after the person granted it. It matches what the SDK's [`accessRequests.getStatus`](/agentic-autofill/partners/sdk#wait-for-the-decision) returns, except for `reference`: in the grant, each `reference` is an object that wraps the reference string, `{ "reference": "<string>" }`, while `getStatus` returns the string itself:

```json theme={null}
{
  "path": "accounts/<account>/credential-broker-access-requests/<request-id>/status",
  "state": "resolved",
  "resolved": [
    {
      "entryId": "<entry-id>",
      "reference": {
        "reference": "accounts/<account>/capability/login/configurations/<configuration-id>"
      }
    }
  ]
}
```

| Field | Type | Description |
| - | - | - |
| `path` | `String` | The status resource path. |
| `state` | `State` | The access request's state: `pending`, `resolved`, `denied`, `failed`, or `expired`. A grant you receive is `resolved`. The other values are the same final states the SDK's `getStatus` and the extension return, so handle them the same way, and note that `expired` isn't returned yet. See [States](/agentic-autofill/partners/approval.md#states). |
| `resolved` | `[ResolvedEntry]` | One entry per granted login. `entryId` names the request entry the login satisfies, and is `nil` for a login the person added beyond the ones you asked for. Match on `entryId`, never on position. `reference` wraps the credential reference string. |

> [!NOTE]
>
> A later version of this protocol is expected to return each `reference` as a plain string, the same as `getStatus`.

## Declare the 1Password types

Access requests and grants use two Uniform Type Identifiers. Both conform to `public.json`.

| Type identifier | Direction |
| - | - |
| `com.1password.access-request` | From your app to 1Password |
| `com.1password.access-grant` | From 1Password to your app |

1Password owns these types, so declare them as imported types in your app's `Info.plist`:

```xml Info.plist theme={null}
<key>UTImportedTypeDeclarations</key>
<array>
    <dict>
        <key>UTTypeIdentifier</key>
        <string>com.1password.access-request</string>
        <key>UTTypeDescription</key>
        <string>1Password Access Request</string>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.json</string>
        </array>
    </dict>
    <dict>
        <key>UTTypeIdentifier</key>
        <string>com.1password.access-grant</string>
        <key>UTTypeDescription</key>
        <string>1Password Access Grant</string>
        <key>UTTypeConformsTo</key>
        <array>
            <string>public.json</string>
        </array>
    </dict>
</array>
```

The integration types refer to these identifiers with `UTType(importedAs:)`, which resolves them correctly only once your `Info.plist` declares them.

## Send the access request

1. Wrap the access request reference in an `OnePasswordAccessRequest` and encode it as JSON.
2. Register the JSON on an `NSItemProvider`, then attach the provider to an `NSExtensionItem`.
3. Create a `UIActivityViewController` with that item, and present it the way your app presents any share sheet.

```swift theme={null}
import UIKit

func makeAccessRequestActivityViewController(
    accessRequestReference: String
) throws -> UIActivityViewController {
    let request = OnePasswordAccessRequest(accessRequestReference: accessRequestReference)
    let requestJSON = try JSONEncoder().encode(request)

    let provider = NSItemProvider()
    provider.registerDataRepresentation(for: .onePasswordAccessRequest) { completion in
        completion(requestJSON, nil)
        return nil
    }

    let item = NSExtensionItem()
    item.attachments = [provider]

    let activityViewController = UIActivityViewController(activityItems: [item], applicationActivities: nil)
    activityViewController.completionWithItemsHandler = { _, completed, returnedItems, error in
        // See "Handle the result".
    }
    return activityViewController
}
```

> [!TIP]
>
> If 1Password doesn't appear in the share sheet, the person can turn it on under **More** in the row of actions.

## Handle the result

UIKit calls `completionWithItemsHandler` when the share sheet closes. What you receive depends on how the request ended:

| Outcome | `completed` | `returnedItems` | `error` |
| - | - | - | - |
| The person granted access | `true` | Contains a `com.1password.access-grant` attachment | `nil` |
| The person declined, or 1Password couldn't process the request | `false` | `nil` | Set |
| The person closed the share sheet without choosing 1Password | `false` | `nil` | `nil` |

### Decode the access grant

Find the attachment that conforms to the access grant type, load its JSON, and decode it as an `OnePasswordAccessGrant`:

```swift theme={null}
enum AccessRequestResultError: Error {
    case missingGrant
}

func loadAccessGrant(from returnedItems: [Any]?) async throws -> OnePasswordAccessGrant {
    let provider = (returnedItems ?? [])
        .compactMap { $0 as? NSExtensionItem }
        .flatMap { $0.attachments ?? [] }
        .first { $0.hasItemConformingToTypeIdentifier(UTType.onePasswordAccessGrant.identifier) }

    guard let provider else {
        throw AccessRequestResultError.missingGrant
    }

    let grantJSON: Data = try await withCheckedThrowingContinuation { continuation in
        _ = provider.loadDataRepresentation(for: .onePasswordAccessGrant) { data, error in
            if let data {
                continuation.resume(returning: data)
            } else {
                continuation.resume(throwing: error ?? AccessRequestResultError.missingGrant)
            }
        }
    }
    return try JSONDecoder().decode(OnePasswordAccessGrant.self, from: grantJSON)
}
```

### Use the grant

Each `reference` in the grant identifies one granted login. It holds no secret, and it works only with the access token and integration key of the connection that created the request, which stay on your side. The extension and the SDK both take the string inside it, `reference.reference`, not the object:

* **To fill in your agent's browser:** pass `reference.reference` to the extension's [`fillCredential`](/agentic-autofill/partners/fill#fill-a-granted-login) as `resourcePath`, with the same connection's access token and integration key.
* **To read the login with the SDK:** pass `reference.reference` to the SDK's read methods. See [Read a granted login](/agentic-autofill/partners/sdk.md#read-a-granted-login).

## Errors

If the person declines, or 1Password can't process the request, the share sheet passes you an error. These are the errors 1Password reports today, the cases of `OnePasswordAccessRequestError`:

| Error | Meaning | What to do |
| - | - | - |
| `.missingRequest` | None of the shared items contained an access request. | Check that the attachment uses `UTType.onePasswordAccessRequest`. |
| `.requestDecodeFailure` | The request is malformed, or 1Password couldn't read its access request reference. | Check that the attachment is a JSON-encoded `OnePasswordAccessRequest` and that the reference is unchanged. |
| `.unsupportedVersion` | This version of 1Password doesn't support the request's `version`. | Ask the person to update 1Password, then try again. |
| `.declined` | The person declined the request, or closed 1Password without granting anything. | Don't retry automatically. |
| `.internalError` | 1Password hit an unexpected error while handling the request. | The access request may still be valid. Try again later without changing it. |

Treat any other error as a failed access request. 1Password may add error cases, and the system can pass you errors of its own.


## Related topics

- [Authorize SSH requests with 1Password](/ssh/agent/authorization.md)
- [Build integrations with 1Password](/get-started/build-integrations.md)
- [1Password developer quickstart](/get-started/developer-quickstart.md)
