> The Markdown source of https://developer.suredocs.ie/getting-started.html. The index of these files is [llms.txt](llms.txt), and the brief for coding agents building an integration is [agents.md](agents.md).

# Getting started

This page takes you from nothing to a PDF filed with a brokerage, using `curl`. Every value in it that you
would replace is obviously fake, and nothing here is a real credential.

## What you need

- **An integration id and a refresh token.** The integration is your software's identity with us, and its
  id looks like `int_example_a1b2c3d4`. The refresh token is the long-lived secret we issued for it. We hand
  both to you through a one-time share that expires; see [Authentication](authentication.md) for how to
  keep the refresh token safe.
- **A brokerage that has granted your integration access.** Try everything against the test broker, whose
  id is `test`, before you use a real brokerage. Ask us to grant your integration access to it.
- **A PDF**, no larger than 24,000,000 bytes, a JPEG or PNG image, no larger than
  10,000,000 bytes, a UTF-8 text file such as a call transcript, no larger than
  2,000,000 bytes, or a WAV or MP3 recording such as a recorded call, no larger than
  24,000,000 bytes. Each is stored as what it is, never converted to another type; see the
  [API reference](reference/index.html) for the full, current list of accepted types.
- `curl` and [`jq`](https://jqlang.org), or any HTTP client you prefer.

## The model in one paragraph

An **integration** is your software. We issue it a **refresh token**. You exchange the refresh token for an
**access token**, which lasts 1 hour and acts for one brokerage only. The brokerage must
have given your integration a **grant**, which lists what it may do, and the access token carries the
scopes both agree on. You send the access token with each call. The brokerage is named in the token and
never in a URL, so a call cannot be aimed at another brokerage by changing a path.

    integration -> refresh token -> access token -> the brokerage's grant

## 1. Set your credentials

```bash
export CLIENT_ID='int_example_a1b2c3d4'
export REFRESH_TOKEN='<the refresh token we gave you>'
export BROKER_ID='test'
```

## 2. Exchange the refresh token for an access token

```bash
curl -sS https://api.suredocs.ie/oauth/token \
  --data-urlencode grant_type=refresh_token \
  --data-urlencode client_id="$CLIENT_ID" \
  --data-urlencode refresh_token="$REFRESH_TOKEN" \
  --data-urlencode broker_id="$BROKER_ID" > token.json
```

The response looks like this:

```json
{
  "access_token": "<the access token, a long opaque string>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<the NEXT refresh token>",
  "scope": "upload_file"
}
```

**The refresh token in the response replaces the one you sent, and the one you sent no longer works.**
Store the new one before you do anything else. In real code you write it to your secret store first and
only then use the access token. [Authentication](authentication.md) explains why, and what to do when a
response is lost.

For this walkthrough, keep both in shell variables. Run these two lines only when the response is the one
above; after an error they would overwrite `REFRESH_TOKEN` with `null`:

```bash
export ACCESS_TOKEN=$(jq -r .access_token token.json)
export REFRESH_TOKEN=$(jq -r .refresh_token token.json)
```

If the request is refused, the body says why. `invalid_grant` means the refresh token is no longer usable;
`invalid_target` means the brokerage does not exist or has not granted your integration access. Every error
is listed in [Authentication](authentication.md).

## 3. Upload a PDF

The file travels base64-encoded inside a JSON body. Encode it into a file first, because a large PDF is
too big to pass on a command line:

```bash
base64 -w0 example.pdf > example.pdf.b64        # on macOS: base64 -i example.pdf -o example.pdf.b64

jq -n --rawfile content example.pdf.b64 \
  '{filename: "example.pdf", contentType: "application/pdf", contentBase64: $content}' |
curl -sS https://api.suredocs.ie/files/v1 \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  --data-binary @-
```

The answer says the document was stored:

```json
{
  "status": "stored",
  "fileId": "file_0123456789abcdef0123456789abcdef",
  "requestId": "2f4a5f8e-9d3c-4b6e-8f4a-0c5b1e7d2a90"
}
```

Send the same request again and the answer is a duplicate. Nothing more is stored, so a retry after a
timeout is always safe:

```json
{
  "status": "duplicate",
  "fileId": "file_0123456789abcdef0123456789abcdef",
  "matchedOn": ["filename", "content"],
  "duplicateOf": "file_0123456789abcdef0123456789abcdef",
  "requestId": "6c1d9a52-3f0b-47a8-a1de-5b7f2c4e8d13"
}
```

A duplicate is decided by content first: the same bytes are a duplicate under any name. We compare with
documents stored in the last 18 days and do not remember for longer, so the same bytes sent
after that are stored again. The same name with different bytes is a different document. It is stored under
a name derived from yours, and the answer says so in `storedAs` and `nameCollisionWith`.

A refusal has one shape whatever went wrong. `reason` is what your code branches on:

```json
{
  "status": 415,
  "reason": "unsupported_file_type",
  "message": "contentType must be one of application/pdf, image/jpeg, text/plain, image/png, audio/wav, audio/mpeg.",
  "requestId": "0b8e3d17-c6a4-4f29-9e5d-7a1c3b5f2e60"
}
```

## What the brokerage sees

The document is classified like any other and reaches the brokerage as a new item in its SureDocs list. It
is from **Partner upload**, and its type is **Unknown**. It carries the filename you sent, so the brokerage
sees that name, and only that brokerage does. Do not put in a filename anything you would not want its staff
to read. A brokerage finds the document by typing "Partner upload" in the list's search box, as it would find
one from any insurer. Opening it shows what you sent: a PDF opens as a PDF, an image as an image, a text
file as text and a recording as a recording, because none is converted into another type on the way in.

## Where next

- Renew the access token before it expires, not before every call: it lasts 1 hour. The
  rules for doing that safely are in [Authentication](authentication.md).
- Read [Rate limits](rate-limits.md) before you build a loop around the upload.
- Every field, limit and error of the upload is in the [API reference](reference/index.html).
