# API Documentation

## Bearer Token & API Key Generation

**Bearer authentication** (also called **token authentication**) is an HTTP **authentication** scheme that involves security **tokens** called **bearer tokens**. LetterSG uses bearer authentication.&#x20;

To generate the token, click on "API Integration" in the navigation bar. From there, click on `Generate API key` and copy the token. Use this key to start using LetterSG's API.&#x20;

<figure><img src="https://4097645561-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUGChNUU8J3OerY8QoGh6%2Fuploads%2F804ytZWcDAb2g6psNCBX%2FScreenshot%202024-03-25%20at%203.59.54%E2%80%AFPM.png?alt=media&#x26;token=70103ade-1ae8-45e6-a271-1c02763204df" alt=""><figcaption></figcaption></figure>

<figure><img src="https://4097645561-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FUGChNUU8J3OerY8QoGh6%2Fuploads%2FsaKWOLxiT1HGXdieEDHP%2FScreenshot%202024-03-25%20at%204.00.07%E2%80%AFPM.png?alt=media&#x26;token=b9d508b3-bf6e-4058-b04c-08ebe444f69e" alt=""><figcaption></figcaption></figure>

## Authentication

LetterSG's API uses APIKey for authentication. User can view and manage API Keys in LetterSG API Dashboard.

Staging secret keys will have `test_v1_`version prefix.

Production secret keys will have `live_v1_`version prefix.

Authentication to the API is performed via bearer auth.

All API requests must be made over HTTPS. Calls made over plain HTTP will fail and requests without authentication will also fail.

To verify that your API key is working, send a curl request:

```bash
curl --location \
--request GET 'https://letters.gov.sg/api/v1/templates/155' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY'
```

This request gets the template details for template 155, our sample template **OGP - Certificate of Curiosity**.

You should see a response containing the details for template 155:

```json
{
    "templateId": 155,
    "fields": [
        "recipient_name"
    ],
    "name": "OGP - Certificate of Curiosity",
    "createdAt": "2023-11-09T04:17:49.631Z",
    "updatedAt": "2023-11-09T04:17:50.000Z"
}
```

## Errors

LetterSG API uses conventional API Error to indicate the success or failure of an API request.

<table><thead><tr><th width="358">Status Codes</th><th>Description</th></tr></thead><tbody><tr><td>200 - OK</td><td>Everything worked as expected.</td></tr><tr><td>302 - Redirect</td><td>(PDF endpoint) Redirects to the presigned URL for PDF.</td></tr><tr><td>400 - Bad Request</td><td>The request was unacceptable, often due to missing a required parameter.</td></tr><tr><td>401 - Unauthorized</td><td>No valid API key provided.</td></tr><tr><td>402 - Request Failed</td><td>The parameters were valid but the request failed.</td></tr><tr><td>404 - Not Found</td><td>The requested resource doesn't exist.</td></tr><tr><td>429 - Too Many Requests</td><td>Too many requests hit the API too quickly. We recommend an exponential backoff of your requests.</td></tr><tr><td>500, 502, 503, 504 - Server Errors</td><td>Something went wrong on LetterSG's API end.</td></tr></tbody></table>

## Rate Limits

Unless otherwise stated, LetterSG allows up to 20 requests per second per user (subject to change).

## Endpoints

| Environment | Endpoint                              |
| ----------- | ------------------------------------- |
| Production  | <https://letters.gov.sg/api/>         |
| Staging     | <https://staging.letters.gov.sg/api/> |

## Get All Templates

To see all templates that you have access to, send a GET request to list all templates. This endpoint also returns template fields that you will need to provide when generating the letters for each template.

```jsx
GET /v1/templates
```

**Request body:**

<table><thead><tr><th width="147">Parameter</th><th width="95.33333333333331">Type</th><th width="134">Default value</th><th width="126">Required?</th><th>Description</th></tr></thead><tbody><tr><td><code>limit</code></td><td>number</td><td>100</td><td>No, optional</td><td>Maximum number of templates to be returned at once</td></tr><tr><td><code>offset</code></td><td>number</td><td>0</td><td>No, optional</td><td>Offset of the first template returned in the collection.</td></tr></tbody></table>

**Returns:**

<pre class="language-jsx"><code class="lang-jsx"><strong>// list of all templates that the user has access to
</strong><strong>[
</strong><strong>    templates: {
</strong>      templateId: number
      fields: string[]
      name: string
      createdAt: string
      updatedAt: string
    }[],
    count: number
]
</code></pre>

**Sample request:**

<pre class="language-bash"><code class="lang-bash"><strong># replace live_v1_YOUR_API_KEY with your actual API key
</strong>curl --location \
--request GET 'https://letters.gov.sg/api/v1/templates?limit=2&#x26;offset=5' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY'
</code></pre>

**Response**:

<pre class="language-json"><code class="lang-json">[
    "templates": [
      {
        "templateId": 234,
        "fields": ["name", "name_2"],
        "name": "Template Name 1",
        "createdAt": "2022-09-19T03:31:00.131Z",
        "updatedAt": "2022-09-19T03:31:00.131Z"
<strong>      },
</strong>      {
        "templateId": 45,
        "fields": ["name", "name_2"],
        "name": "Template Name 2",
        "createdAt": "2022-09-19T03:31:00.131Z",
        "updatedAt": "2022-09-19T03:31:00.131Z"
     }
<strong>    ],
</strong>    "count": 40
]
</code></pre>

## Get Template Details by ID

To see the details of a template, send a GET request with the template ID of the template. This endpoint returns the template fields you need to provide when generating a letter.

```jsx
GET /v1/templates/:id
```

**Request body**

<table><thead><tr><th width="155">Parameter</th><th width="141.33333333333331">Type</th><th width="135">Required?</th><th></th></tr></thead><tbody><tr><td><code>id</code></td><td>number</td><td>Yes, required</td><td>ID of the template</td></tr></tbody></table>

**Returns:**

<pre class="language-jsx"><code class="lang-jsx"><strong>// details of template
</strong><strong>{
</strong>    templateId: number
    fields: string[] 
    name: string
    createdAt: string
    updatedAt: string
}
</code></pre>

**Sample request:**

```bash
# replace live_v1_YOUR_API_KEY with your actual API key
curl --location \
--request GET 'https://letters.gov.sg/api/v1/templates/155' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY'
```

**Response**:

```json
{
    "templateId": 155,
    "fields": [
        "recipient_name"
    ],
    "name": "OGP - Certificate of Curiosity",
    "createdAt": "2023-11-09T04:17:49.631Z",
    "updatedAt": "2023-11-09T04:17:50.000Z"
}
```

## (Single) Create Letter

To create a new letter, send a POST request specifying the template you would like to use, and the provide the relevant template fields for the letter. If you are unsure which template fields are required for the template, see [Get Template Details By Id](#get-template-details-by-id).

The endpoint returns the link to the created letter, the public id of the letter, and the html of the created letter. If you need to [query the status of the letter or retrieve information about the letter](#get-letter-metadata), please store the public id of the letter as this is the unique identifier for this letter.

```jsx
POST /v1/letters
```

**Request body:**

<table><thead><tr><th width="192.33333333333331">Property</th><th width="94">Type</th><th width="112">Required?</th><th>Description</th></tr></thead><tbody><tr><td>templateId</td><td>number</td><td>Yes, required</td><td>ID of the template</td></tr><tr><td>letterParams</td><td>JSON</td><td>Yes, required</td><td>Key-value pairs of the letter, based on the params contained in the template.<br><br>If params values are not available, param must still be provided but you may use an empty space to indicate that the param is not available e.g.<code>"name": " "</code></td></tr><tr><td>notificationParams</td><td>JSON</td><td>No, optional</td><td><p>Controls whether Letters should notify the recipient on your behalf.</p><ul><li>If provided — Letters sends the letter link to the recipient via the specified channel. notificationMethod must be one of <code>EMAIL</code> or <code>SMS</code>. See the object format below.</li><li>If omitted — The letter is still created and a public URL is generated, but no email or SMS is sent. Use this if you want to deliver the letter link through your own system. The URL can be constructed from the <code>letterLink</code> returned in the response.</li></ul></td></tr></tbody></table>

* Notification params, if provided, should look like so:

<table><thead><tr><th width="194">Property</th><th width="149">Type</th><th width="142">Required</th><th>Description</th></tr></thead><tbody><tr><td>recipient</td><td>string</td><td>Yes, required</td><td>Contact of recipient, can be either phone number or email, provided as string. <br><br>Phone numbers should start with either 8 or 9. Only local phone numbers supported for now.</td></tr><tr><td>notificationMethod</td><td>ENUM ("SMS" or "EMAIL") </td><td>Yes, required</td><td>Case sensitive</td></tr></tbody></table>

**Returns**:

<pre class="language-jsx"><code class="lang-jsx"><strong>// created letter
</strong><strong>{
</strong>    publicId: string
    issuedLetter: string
    letterLink: string
    createdAt: string
    firstReadAt: string
    notificationStatus?: NotificationStatus
    recipient?: string
}
</code></pre>

**Sample request:**

<pre class="language-bash"><code class="lang-bash"># replace live_v1_YOUR_API_KEY with your actual API key

# sample request to create a letter only, without SMS or EMAIL notification
curl --location 'https://letters.gov.sg/api/v1/letters' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY' \
--data '{
    "templateId": 155,
    "letterParams": {
        "recipient_name": "Maxine Marcella"
    }
}'

# sample request with SMS notification
# replace YOUR_PHONE_NUMBER_STARTING_WITH_8_OR_9 with your phone number
<strong># this sends a real SMS out, please test with your own phone number
</strong><strong>curl --location 'https://letters.gov.sg/api/v1/letters' \
</strong>--header 'Content-Type: application/json' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY' \
--data '{
    "templateId": 155,
    "letterParams": {
        "recipient_name": "Maxine Marcella"
    },
    "notificationParams": {
        "recipient": "YOUR_PHONE_NUMBER_STARTING_WITH_8_OR_9",
        "notificationMethod": "SMS"
    }
}'

# sample request with EMAIL notification
# replace YOUR_EMAIL with your email address
# this sends a real email out, please test with your own email address
curl --location 'https://letters.gov.sg/api/v1/letters' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY' \
--data '{
    "templateId": 155,
    "letterParams": {
        "recipient_name": "Maxine Marcella"
    },
    "notificationParams": {
        "recipient": "YOUR_EMAIL",
        "notificationMethod": "EMAIL"
    }
}'
</code></pre>

**Response:**

```json
{
    "publicId": "2here-8o5td-9g4b6-xfd86",
    "letterLink": "https://letters.gov.sg/2here-8o5td-9g4b6-xfd86",
    "createdAt": "Fri May 17 2024",
    "issuedLetter": "<div style=\"background-image: url('https://file.go.gov.sg/3f5f3u.png'); background-size: cover; aspect-ratio: 1/1.435; color: black;\">\n<div style=\"position: absolute; text-align: center; width: 100%; top: 45.8%; transform: translate(0, -50%);\">\n<div style=\"font-family: georgia, palatino, serif; font-size: 26px; line-height: 1.5; letter-spacing: 0.05rem;\"><span style=\"font-size: 36px;\">CERTIFICATE OF</span><br><span style=\"font-size: 36px;\">CURIOSITY</span></div>\n<br>\n<div style=\"margin-top: 10px; font-family: verdana, geneva, sans-serif; letter-spacing: 0.2rem; color: #666c7a; font-size: 12px;\"><span style=\"font-size: 14px;\">AWARDED TO</span></div>\n<div style=\"font-family: symbol; font-size: 45px; margin-top: 50px; margin-left: 3rem; margin-right: 3rem;\"><span style=\"font-size: 58px; font-family: 'times new roman', times, serif;\"><em>Maxine Marcella</em></span></div>\n<br>\n<div style=\"font-family: Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.5; margin-top: 15px;\"><span style=\"font-size: 18px;\">for the successful completion of sending out your e-letter</span><br><span style=\"font-size: 18px;\">from <strong>LetterSG</strong></span></div>\n<br>\n<table style=\"width: 100%; text-align: center; margin-top: 15px;\"><colgroup> <col width=\"10%\"> <col width=\"10%\"> <col width=\"10%\"> <col width=\"10%\"> <col width=\"10%\"> </colgroup>\n<tbody>\n<tr style=\"font-family: 'Brush Script MT', cursive; font-size: 24px;\">\n<td>&nbsp;</td>\n<td style=\"border-bottom: 2px solid black; padding-bottom: 3px;\"><span style=\"color: rgb(0, 0, 0); font-size: 36px;\">Yung</span></td>\n<td>&nbsp;</td>\n<td style=\"border-bottom: 2px solid black;\"><span style=\"color: rgb(0, 0, 0); font-size: 36px;\">A</span></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td>&nbsp;</td>\n<td style=\"vertical-align: top; padding-top: 12px; font-family: Arial, Helvetica, sans-serif; font-size: 12px;\"><span style=\"font-size: 16px;\">Yung Guo En</span><br>\n<div style=\"font-family: verdana, geneva, sans-serif; letter-spacing: 0.1rem; font-size: 8px; padding-top: 5px;\"><span style=\"font-size: 10px;\">LETTERSG TRAINER</span></div>\n</td>\n<td colspan=\"3\" style=\"vertical-align: top; padding-top: 12px; font-family: Arial, Helvetica, sans-serif; font-size: 12px;\"><span style=\"font-size: 16px;\">Abbas Noor Bin Naqib Riduan</span><br>\n<div style=\"font-family: verdana, geneva, sans-serif; letter-spacing: 0.1rem; font-size: 8px; padding-top: 5px;\"><span style=\"font-size: 10px;\">LETTERSG ASSISTANT TRAINER</span></div>\n</td>\n</tr>\n</tbody>\n</table>\n</div>\n</div>"
}
```

**Potential error responses (non-exhaustive):**

```json
{
    "statusCode": 404,
    "message": "Template not found",
    "error": "Not Found"
}
```

```json
// extra letter param in request, serial_number
{
    "message": "Invalid letter params.",
    "error": [
        {
            "id": 0,
            "param": "serial_number",
            "message": "Invalid attribute in param",
            "displayedErrorMessage": " is an extra field"
        }
    ]
}

// missing letter param in request, recipient_name
{
    "message": "Invalid letter params.",
    "error": [
        {
            "id": 0,
            "param": "recipient_name",
            "message": "Missing param",
            "displayedErrorMessage": " field is missing"
        }
    ]
}
```

## (Bulk) Create letters

To create a batch of letters, send a POST request specifying the template you would like to use, and the provide a list of template fields, one set of template fields for each letter to be generated. If you are unsure which template fields are required for the template, see [Get Template Details By Id](#get-template-details-by-id).&#x20;

The endpoint returns the id of the created batch of letters. If you need to [query the status of the batch](https://guide.letters.gov.sg/developer-guide/api-documentation#get-batch-metadata), please store the id of the batch as this is the identifier for this batch.

This endpoint is generates a max of 500 letters at one time. Supported TPS: 1 transaction per second per user.&#x20;

Please contact us if you need us to increase the rate limits.

```
POST /v1/letters/bulks
```

**Request body:**

<table><thead><tr><th width="192.33333333333331">Property</th><th width="93">Type</th><th width="98">Required?</th><th></th></tr></thead><tbody><tr><td>templateId</td><td>number</td><td>Yes, required</td><td>ID of the template</td></tr><tr><td>lettersParams</td><td>JSON</td><td>Yes, required</td><td>An array containing key-value pairs for each letter, based on the params contained in the template.<br><br>If param values are not available, param must still be provided but you may use an empty space to indicate that the param is not available e.g.<code>"name": " "</code></td></tr><tr><td>notificationMethod</td><td>string</td><td>No, optional</td><td><p>Controls whether Letters should notify the<br>recipients on your behalf.</p><ul><li>If provided — Letters sends the letter link to each recipient via the specified channel. notificationMethod must be one of <code>EMAIL</code> or <code>SMS</code>. recipients must be provided alongside it. See the object format below.</li><li><p>If omitted — The letters are still created and stored, but no email or SMS is sent. Use this if you want to deliver the letter links through your own system. To retrieve the links: </p><ul><li>Use the <code>batchId</code> from the response to call <a href="#get-batch-metadata">Get Batch Metadata</a>, which returns a <code>letterPublicIds</code> array. </li><li>For each <code>publicId</code>, call <a href="#get-letter-metadata">Get Single Letter Metadata </a>to retrieve its full <code>letterLink</code>.</li></ul></li></ul></td></tr><tr><td>recipients</td><td>JSON</td><td>No, optional</td><td><p>An array of recipients that correspond to the letters. This list is <strong>required</strong> if a notification is to be sent. Each recipient will be notified via the method specified at the batch level.</p><p></p><p>If notificationMethod is <code>"EMAIL"</code> or <code>"SMS"</code>, <code>recipients</code> must be provided.<br><br>Contact of recipient can be either phone number or email, provided as strings. <br><br>Phone numbers should start with either 8 or 9. Only local phone numbers supported for now.</p></td></tr></tbody></table>

**Returns:**

```jsx
// created letter
{ batchId: number }
```

**Sample request:**

```bash
# sample request to create letters only, without SMS or EMAIL notification
curl --location 'https://letters.gov.sg/api/v1/letters' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY' \
--data '{
    "templateId": 155,
    "lettersParams": [
        {
            "recipient_name": "Test1"
        },
        {
            "recipient_name": "Test2"
        }
    ]
}'

# sample request with SMS notifications
# replace YOUR_PHONE_NUMBER with your phone numbers
# this sends a real sms out, please test with your own phone numbers
curl --location 'https://letters.gov.sg/api/v1/letters' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY' \
--data '{
    "templateId": 155,
    "lettersParams": [
        {
            "recipient_name": "Test1"
        },
        {
            "recipient_name": "Test2"
        }
    ],
    "notificationMethod": "SMS",
    "recipients": ["YOUR_PHONE_NUMBER", "YOUR_PHONE_NUMBER"]
}'


# sample request with EMAIL notification
# replace YOUR_EMAIL with your email address
# this sends a real email out, please test with your own email address
curl --location 'https://letters.gov.sg/api/v1/letters' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY' \
--data '{
    "templateId": 155,
    "lettersParams": [
        {
            "recipient_name": "Test1"
        },
        {
            "recipient_name": "Test2"
        }
    ],
    "notificationMethod": "EMAIL",
    "recipients": ["YOUR_EMAIL", "YOUR_EMAIL"]
}'
```

**Response:**

```json
{
    batchId: 3
}
```

**Potential error responses (non-exhaustive):**

```typescript
// 404 Not Found errors 

// template does not exist
{
    "message": "Template not found",
}
```

<pre class="language-typescript"><code class="lang-typescript">// 400 Bad Request errors 

// invalid, missing or extra params
<strong>{
</strong>    "message": "Invalid letter params/ recipients",
    "errors": [
        {
            "id": 0,
            "errorType": "Invalid attribute in param",
            "message": "hello is an extra field"
        },
        {
            "id": 0,
            "errorType": "Missing param",
            "message": "recipient_name field is missing"
        },
        {
            "id": 1,
            "errorType": "Missing param",
            "message": "serial_number field is missing"
        }
    ]
}

{
    "message": "Notification method must be one of 'EMAIL', 'SMS'",
}

// invalid recipients or missing recipients
{
    "message": "Invalid letter params/ recipients",
    "errors": [
        {
            "id": 1,
            "errorType": "Invalid phone number",
            "message": "Phone Number should be local SG handphone number"
        },
        {
            "id": 2,
            "errorType": "Missing param",
            "message": "Phone Number field is missing"
        },
}
</code></pre>

## Get Letter Metadata

To retrieve the status of the letter, send a GET request with the public id of the letter that you would like to retrieve metadata for. You are only able to retrieve the metadata of letters that you have access to.

```jsx
GET /v1/letters/:publicId 
```

**Request Query Parameters:**

<table><thead><tr><th width="139">Parameter</th><th width="93.33333333333331">Type</th><th width="140">Required?</th><th>Description</th></tr></thead><tbody><tr><td><code>publicId</code></td><td>string</td><td>Yes, required</td><td>Letter ID for fetching the status e.g. <em>xxyyy-12345-ddsss-d3454</em></td></tr></tbody></table>

**Returned object**:

```jsx
// letter metadata
{
     publicId: string
     issuedLetter: string
     letterLink: string
     createdAt: string
     firstReadAt?: string
     notificationStatus?: NotificationStatus
     recipient?: string
}
```

**Explanation of fields returned:**

<table><thead><tr><th width="183">Field Returned</th><th width="256">What it means</th><th>Example</th></tr></thead><tbody><tr><td>publicId</td><td>Letter ID for fetching the read status </td><td> xxyyy-12345-ddsss-d3454</td></tr><tr><td>letterLink</td><td>Original link of the letter</td><td>https://letters.gov.sg/letters/public-letter-id</td></tr><tr><td>createdAt</td><td>Timestamp of letter creation</td><td><pre><code>2022-09-19T03:31:00.131Z
</code></pre></td></tr><tr><td>firstReadAt</td><td>(Optional) Timestamp of first read of letter, if letter is read</td><td><pre><code>2022-09-19T03:31:00.131Z
</code></pre></td></tr><tr><td>notificationStatus</td><td>(Optional) Status of the notification that was sent, this could be "SENT", "FAILED", "INVALID_RECIPIENT", "PENDING"</td><td>"SENT"</td></tr><tr><td>recipient</td><td>(Optional) String of the recipient's info</td><td>abcss@example.com</td></tr></tbody></table>

**Sample Request:**

```bash
# replace live_v1_YOUR_API_KEY with your actual API key
curl --location \
--request GET 'https://letters.gov.sg/api/v1/letters/2here-8o5td-9g4b6-xfd86' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY'
```

**Returns:**

```json
{
    "publicId": "2here-8o5td-9g4b6-xfd86",
    "letterLink": "https://letters.gov.sg/2here-8o5td-9g4b6-xfd86",
    "createdAt": "Fri May 17 2024",
    "issuedLetter": "<div style=\"background-image: url('https://file.go.gov.sg/3f5f3u.png'); background-size: cover; aspect-ratio: 1/1.435; color: black;\">\n<div style=\"position: absolute; text-align: center; width: 100%; top: 45.8%; transform: translate(0, -50%);\">\n<div style=\"font-family: georgia, palatino, serif; font-size: 26px; line-height: 1.5; letter-spacing: 0.05rem;\"><span style=\"font-size: 36px;\">CERTIFICATE OF</span><br><span style=\"font-size: 36px;\">CURIOSITY</span></div>\n<br>\n<div style=\"margin-top: 10px; font-family: verdana, geneva, sans-serif; letter-spacing: 0.2rem; color: #666c7a; font-size: 12px;\"><span style=\"font-size: 14px;\">AWARDED TO</span></div>\n<div style=\"font-family: symbol; font-size: 45px; margin-top: 50px; margin-left: 3rem; margin-right: 3rem;\"><span style=\"font-size: 58px; font-family: 'times new roman', times, serif;\"><em>Maxine Marcella</em></span></div>\n<br>\n<div style=\"font-family: Arial, Helvetica, sans-serif; font-size: 14px; line-height: 1.5; margin-top: 15px;\"><span style=\"font-size: 18px;\">for the successful completion of sending out your e-letter</span><br><span style=\"font-size: 18px;\">from <strong>LetterSG</strong></span></div>\n<br>\n<table style=\"width: 100%; text-align: center; margin-top: 15px;\"><colgroup> <col width=\"10%\"> <col width=\"10%\"> <col width=\"10%\"> <col width=\"10%\"> <col width=\"10%\"> </colgroup>\n<tbody>\n<tr style=\"font-family: 'Brush Script MT', cursive; font-size: 24px;\">\n<td>&nbsp;</td>\n<td style=\"border-bottom: 2px solid black; padding-bottom: 3px;\"><span style=\"color: rgb(0, 0, 0); font-size: 36px;\">Yung</span></td>\n<td>&nbsp;</td>\n<td style=\"border-bottom: 2px solid black;\"><span style=\"color: rgb(0, 0, 0); font-size: 36px;\">A</span></td>\n<td>&nbsp;</td>\n</tr>\n<tr>\n<td>&nbsp;</td>\n<td style=\"vertical-align: top; padding-top: 12px; font-family: Arial, Helvetica, sans-serif; font-size: 12px;\"><span style=\"font-size: 16px;\">Yung Guo En</span><br>\n<div style=\"font-family: verdana, geneva, sans-serif; letter-spacing: 0.1rem; font-size: 8px; padding-top: 5px;\"><span style=\"font-size: 10px;\">LETTERSG TRAINER</span></div>\n</td>\n<td colspan=\"3\" style=\"vertical-align: top; padding-top: 12px; font-family: Arial, Helvetica, sans-serif; font-size: 12px;\"><span style=\"font-size: 16px;\">Abbas Noor Bin Naqib Riduan</span><br>\n<div style=\"font-family: verdana, geneva, sans-serif; letter-spacing: 0.1rem; font-size: 8px; padding-top: 5px;\"><span style=\"font-size: 10px;\">LETTERSG ASSISTANT TRAINER</span></div>\n</td>\n</tr>\n</tbody>\n</table>\n</div>\n</div>",
    "firstReadAt": "Fri May 17 2024"
}
```

## Get Batch Metadata

To retrieve the status of the letter batch, send a GET request with the batch id that you would like to retrieve metadata for. You are only able to retrieve the metadata of batches that you have access to.

```jsx
GET /v1/batches/:id 
```

**Request Query Parameters:**

<table><thead><tr><th width="145">Parameter</th><th width="173.33333333333331">Type</th><th>Required?</th><th>Description</th></tr></thead><tbody><tr><td><code>id</code></td><td>number</td><td>Yes, required</td><td>ID of batch</td></tr></tbody></table>

**Returned object**:

```jsx
// letter metadata
{
    batchId: number,
    templateId: number,
    createdAt: Date,
    totalCount: number,
    readCount: number,
    notificationMethod: enum("SMS", "EMAIL"),
    "letterPublicIds": string[]
}
```

**Explanation of fields returned:**

<table><thead><tr><th width="192">Field Returned</th><th width="256">What it means</th><th>Example</th></tr></thead><tbody><tr><td>batchId</td><td>ID of batch issued</td><td> 40091</td></tr><tr><td>templateId</td><td>ID of template used to issue batch</td><td>155</td></tr><tr><td>totalCount</td><td>Total number of letters in batch</td><td><pre><code>2022-09-19T03:31:00.131Z
</code></pre></td></tr><tr><td>readCount</td><td>Number of letters that have been read in batch</td><td><pre><code>2022-09-19T03:31:00.131Z
</code></pre></td></tr><tr><td>notificationMethod</td><td>(Optional) Method that letters sent via, could be "SMS", "EMAIL"</td><td>"SMS"</td></tr><tr><td>letterPublicIds</td><td><p>Array of public id of the letters in the batch. </p><p></p><p>You may use letter publicId to <a href="https://guide.letters.gov.sg/developer-guide/api-documentation#get-letter-metadata">retrieve metadata about the letter</a></p></td><td><pre><code>["lkq15-8w7ur-rznn5-kigvo"]
</code></pre></td></tr></tbody></table>

**Sample request:**

```bash
curl --location \
--request GET 'https://letters.gov.sg/api/v1/batches/40091' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY'
```

**Response:**

```typescript
{
    "batchId": 40091,
    "templateId": 155,
    "createdAt": "Mon, 27 Jan 2025 09:37:24 GMT",
    "totalCount": 1,
    "readCount": 0,
    "notificationMethod": "EMAIL",
    "letterPublicIds": [
        "lkq15-8w7ur-rznn5-kigvo"
    ]
}
```

## Get Downloadable Letter PDF (Beta)

To download the issued letter as a PDF for archiving, send a GET request with the publicId of the letter.&#x20;

{% hint style="info" %}
**Note:** We may choose to further rate limit this endpoint in the future. This endpoint currently is offered in beta, and may not be always offered as a synchronous response.
{% endhint %}

<pre class="language-jsx"><code class="lang-jsx"><strong>GET /v1/letters/:publicId/pdfs
</strong></code></pre>

**Request Query Parameters:**

<table><thead><tr><th width="188">Parameter</th><th width="201.33333333333331">Type</th><th width="178">Default value</th><th>Required?</th></tr></thead><tbody><tr><td><code>publicId</code></td><td>string</td><td>nil</td><td>Yes, required</td></tr></tbody></table>

**Returns**:

Redirects to presigned URL link with a 1-hour expiry (returning a 302 status).&#x20;

```jsx
// url to download link
{
  presignedUrl: string
}
```

**Sample request:**

```bash
# downloads PDF as 2here-8o5td-9g4b6-xfd86.pdf on your device
# replace live_v1_YOUR_API_KEY with your actual API key
curl --location \
--request GET 'https://letters.gov.sg/api/v1/letters/2here-8o5td-9g4b6-xfd86/pdfs' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY' --output 2here-8o5td-9g4b6-xfd86.pdf
```

## Others

**How can I whitelist LetterSG / What is LetterSG's IP address?**\
LetterSG endpoints reside in the internet zone, so you can make the request with your API key.  As we are being served by Cloudflare DNS, you may whitelist this range of Cloudflare's IP addresses ([see here](https://www.cloudflare.com/en-gb/ips/)).

## Keen on APIs?

For more information on integrating your systems with LetterSG, reach out to us at **<support@letters.gov.sg>**&#x20;
