# 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**. ForEduSG 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 ForEduSG's API. &#x20;

<figure><img src="https://1140982890-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MWhLZucP_q7QM7hLFlJ%2Fuploads%2FUP8HE5hf5eUwwQC69Nxb%2FScreenshot%202022-11-24%20at%205.54.28%20PM.png?alt=media&#x26;token=6f16984e-6ddb-499f-b8c4-a7b05a6d3ff7" alt=""><figcaption><p> </p></figcaption></figure>

<figure><img src="https://1140982890-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MWhLZucP_q7QM7hLFlJ%2Fuploads%2F7f8qQhvLwORPYKmQrSu9%2FScreenshot%202022-11-24%20at%205.54.36%20PM.png?alt=media&#x26;token=07e80f8c-2562-4f78-a536-cd01a244b34d" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
**Keep the bearer token** **safe:** You should not share the bearer token with anyone. Use services like 1Password to store it.&#x20;
{% endhint %}

## Authentication

ForEduSG's API uses APIKey for authentication. User can view and manage API Keys in ForEduSG's 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.

```powershell
curl --location --request POST 'https://for.edu.sg/api/v1/urls' \
--header 'Authorization: Bearer live_v1_YOUR_API_KEY'
```

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

## Errors

ForEduSG's API uses conventional API Error to indicate the success or failure of an API request.

| Status Codes                       | Description                                                                                      |
| ---------------------------------- | ------------------------------------------------------------------------------------------------ |
| 200 - OK                           | Everything worked as expected.                                                                   |
| 400 - Bad Request                  | The request was unacceptable, often due to missing a required parameter.                         |
| 401 - Unauthorized                 | No valid API key provided.                                                                       |
| 402 - Request Failed               | The parameters were valid but the request failed.                                                |
| 404 - Not Found                    | The requested resource doesn't exist.                                                            |
| 429 - Too Many Requests            | Too many requests hit the API too quickly. We recommend an exponential backoff of your requests. |
| 500, 502, 503, 504 - Server Errors | Something went wrong on ForEduSG's API end.                                                      |

```json
{
  "message": "Unauthorized"
}
```

## Rate Limits

ForEduSG supports a default rate limit of 5 requests per second for each user. If you require higher rate limits, please write to us at <go@open.gov.sg> with more details.

If you have exceeded your rate limit, your incoming requests will be blocked for 10 seconds as a cooling off period. We recommend throttling your requests and implementing exponential backoff for retries to ensure that your requests can be accepted.

## Endpoints

| Environment | Endpoint                                                      |
| ----------- | ------------------------------------------------------------- |
| Production  | [https://for.edu.sg/api/](https://go.gov.sg/)                 |
| Staging     | [https://staging.for.edu.sg/api/](https://staging.go.gov.sg/) |

## Get Urls

```jsx
GET /v1/urls?...
```

**Request Query Parameters:**

<table><thead><tr><th>Parameter</th><th width="201.33333333333331">Type</th><th>Default value</th><th>Required?</th></tr></thead><tbody><tr><td><code>limit</code></td><td>number</td><td>1000</td><td>No, optional</td></tr><tr><td><code>offset</code></td><td>number</td><td>0</td><td>No, optional</td></tr><tr><td><code>searchText</code></td><td>string</td><td>nil</td><td>No, optional</td></tr><tr><td><code>state</code></td><td>enum (”ACTIVE”, “INACTIVE”)</td><td>nil</td><td>No, optional</td></tr><tr><td><code>orderBy</code></td><td>enum (”createdAt”, “clicks”)</td><td>createdAt</td><td>No, optional</td></tr><tr><td><code>sortDirection</code></td><td>enum (”desc”, “asc”)</td><td>desc</td><td>No, optional</td></tr><tr><td><code>isFile</code></td><td>boolean</td><td>nil</td><td>No, optional</td></tr></tbody></table>

Note: Max values for `limit` = 1000

**Returns**:

```json
{
  "urls": [
    {
      "shortUrl": "197abc",
      "longUrl": "https://link.com",
      "state": "ACTIVE",
      "clicks": 0,
      "createdAt": "2022-09-19T03:31:00.131Z",
      "updatedAt": "2022-09-19T03:31:00.131Z"
    }
  ],
  "count": 1
}
```

**Explanation of fields returned:**

<table><thead><tr><th width="172">Field Returned</th><th width="234">What it means</th><th>Example</th></tr></thead><tbody><tr><td><code>shortUrl</code></td><td>Short link name</td><td>itesurvey<br><br>(ie. full URL will look like https://for.edu.sg/itesurvey)</td></tr><tr><td><code>longUrl</code></td><td>Original link</td><td>https://google.com</td></tr><tr><td><code>state</code></td><td>Whether short link is active or inactive</td><td>"ACTIVE" or "INACTIVE"</td></tr><tr><td><code>clicks</code></td><td>Number of clicks on the short link since creation</td><td>888</td></tr><tr><td><code>count</code></td><td>Counts the total number of links matching search filters (except <code>limit</code> and <code>offset</code>) </td><td>8</td></tr></tbody></table>

## Create Url

```jsx
POST /v1/urls
```

**Request body:**

| Property   | Type   | Required?     |
| ---------- | ------ | ------------- |
| `longUrl`  | string | Yes, required |
| `shortUrl` | string | No, optional  |

* `longUrl` has to start with https\://
* If no `shortUrl` is provided, then a random 8 character alphanumeric shortUrl name will be generated for the link.

**Returns**:

```json
{
  "shortUrl": "197abc",
  "longUrl": "https://link.com",
  "state": "ACTIVE",
  "clicks": 0,
  "createdAt": "2022-09-19T03:31:00.131Z",
  "updatedAt": "2022-09-19T03:31:00.131Z"
}
```

```json
{
  "message": "Short link \\"asd\\" is already used.",
  "type": "ShortUrlError"
}
```

## Update Url

```jsx
PATCH /v1/urls/{shortUrl}
```

Note: Indicate shortUrl name you wish to update in above PATCH request, ie. {shortUrl}

**Request body:**

<table><thead><tr><th>Property</th><th width="278.3333333333333">Type</th><th>Required?</th></tr></thead><tbody><tr><td><code>longUrl</code></td><td>string</td><td>No, optional</td></tr><tr><td><code>state</code></td><td>enum (”ACTIVE”, “INACTIVE”)</td><td>No, optional</td></tr></tbody></table>

* &#x20;`longUrl` has to start with https\://

**Returns**:

```jsx
{
  "shortUrl": "197abc",
  "longUrl": "https://link.com",
  "state": "ACTIVE",
  "clicks": 0,
  "createdAt": "2022-09-19T03:31:00.131Z",
  "updatedAt": "2022-09-19T03:31:00.131Z"
}
```
