# Complete an attachment upload session

> **POST** `https://api.us.nylas.com/v3/grants/{grant_id}/attachment-uploads/{attachment_id}/complete`

Source: https://developer.nylas.com/docs/reference/api/attachments/complete-attachment-upload-session/

Finalize an attachment upload session after the file has been uploaded to the pre-signed URL.
Nylas verifies that the upload succeeded in storage and, if `size` was declared on session
creation, that the uploaded byte count matches the declared size.

After completion, reference the attachment in a [send](/docs/reference/api/messages/send-message/)
or [draft](/docs/reference/api/drafts/put-drafts-id/) request by passing
`{ "id": "<attachment_id>" }` in the `attachments` array.

**Retention note:** By default, the uploaded file is deleted from storage when `expires_at`
passes (one hour after session creation). Plan your send to occur inside this window. The
attachment metadata row is retained for 60 days for observability.

For the full upload flow, see
[Send large attachments](/docs/v3/email/send-large-attachments/).

**Authentication:** ACCESS_TOKEN, NYLAS_API_KEY

## Parameters

### Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `grant_id` | string | Yes | The ID of the grant the upload session belongs to. |
| `attachment_id` | string | Yes | The ID of the attachment upload session to complete. |

## Responses

### 200 - Upload session completed successfully. The attachment is now ready to use in a send or draft.

- `request_id` (string)
- `data` (object)
  - `attachment_id` (string) - The attachment ID. Pass this value as `{ "id": "<attachment_id>" }` in the `attachments` array of a send or draft request.
  - `grant_id` (string)
  - `status` (string) - Upload session status. Always `ready` on success.

### 401 - Unauthorized

- `request_id` (string) - The request ID.
- `error` (object) - The response error object.
  - `type` (string) - The error type.
  - `message` (string) - The error message.
  - `provider_error` (object) - The error from the provider.

### 404 - The `attachment_id` does not exist.

- `request_id` (string)
- `error` (object)
  - `type` (string)
  - `message` (string)

### 409 - The upload session has already been completed (`status: ready`) or has failed (`status: failed`).

- `request_id` (string)
- `error` (object)
  - `type` (string)
  - `message` (string)

### 410 - The upload session has expired. Returned when the session was previously marked `expired`
by the cleanup job, or when `expires_at` has already passed at the time `/complete` is called.

- `request_id` (string)
- `error` (object)
  - `type` (string)
  - `message` (string)

### 422 - Upload verification failed. Returned when the file is not found in storage,
when the uploaded size does not match the `size` declared on session creation, or
when no `size` was declared and the uploaded object is empty.

- `request_id` (string)
- `error` (object)
  - `type` (string)
  - `message` (string)

### 500 - Internal error. Returned on storage or database failures during verification.

- `request_id` (string)
- `error` (object)
  - `type` (string)
  - `message` (string)

## Code samples

### cURL

```bash
curl --request POST \
  --url 'https://api.us.nylas.com/v3/grants/<GRANT_ID>/attachment-uploads/<ATTACHMENT_ID>/complete' \
  --header 'Accept: application/json' \
  --header 'Authorization: Bearer <NYLAS_API_KEY_OR_ACCESS_TOKEN>'
```

### Node.js SDK

```javascript
import Nylas from "nylas";

const nylas = new Nylas({
  apiKey: "<NYLAS_API_KEY>",
  apiUri: "<NYLAS_API_URI>",
});

async function completeUploadSession() {
  try {
    const result = await nylas.attachments.completeUploadSession({
      identifier: "<NYLAS_GRANT_ID>",
      attachmentId: "<ATTACHMENT_ID>",
    });

    console.log("Completed:", result);
  } catch (error) {
    console.error("Error completing upload session:", error);
  }
}

completeUploadSession();

```
