<p>Appwrite supports multiple protocols for accessing the server, including <a href="/docs/rest">REST</a>, <a href="/docs/graphql">GraphQL</a>, and <a href="/docs/realtime">Realtime</a>.</p>

<p>The REST API allows you to access your Appwrite server through HTTP requests without the needing an SDK. Each endpoint in the API represents a specific operation on a specific resource.</p>

<h2><a href="#headers" id="headers">Headers</a></h2>
<p>Appwrite's REST APIs expect certain headers to be included with each request:</p>

<table class="full text-size-small">
    <thead>
    <tr>
        <td style="width: 300px">Header</td>
        <td style="width: 80px"></td>
        <td>Description</td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td><code>X-Appwrite-Project: [PROJECT-ID]</code></td>
        <td><span class="tag red">required</span></td>
        <td>The ID of your Appwrite project</td>
    </tr>
    <tr>
        <td><code>Content-Type: application/json</code></td>
        <td><span class="tag red">required</span></td>
        <td>Content type of the HTTP request.</td>
    </tr>
    <tr>
        <td><code>X-Appwrite-Key: [API-KEY]</code></td>
        <td><span class="tag">optional</span></td>
        <td>API key used for server authentication. <b>Do not use API keys in client applications.</b></td>
    </tr>
    <tr>
        <td><code>X-Appwrite-JWT: [TOKEN]</code></td>
        <td><span class="tag">optional</span></td>
        <td>Token used for JWT authentication, tokens can be generated using the <a href="/docs/client/account#accountCreateJWT">Create JWT</a> endpoint.</b></td>
    </tr>
    <tr>
        <td><code>X-Appwrite-Response-Format: [VERSION-NUMBER]</code></td>
        <td><span class="tag">optional</span></td>
        <td>Version number used for backward compatibility. The response will be formatted to be compatible with the provided version number.</td>
    </tr>
    <tr>
        <td><code>X-Fallback-Cookies: [FALLBACK-COOKIES]</code></td>
        <td><span class="tag">optional</span></td>
        <td>Fallback cookies used in scenarios where browsers do not allow third-party cookies. Often used when there is no <a href="/docs/custom-domains">Custom Domain</a>.</td>
    </tr>
    </tbody>
</table>

<h2><a href="#no-headers" id="no-headers">Using Appwrite Without Headers</a></h2>
<p>Some use cases do not allow custom headers, such as embedding images from Appwrite in HTML. In these cases, you can provide the Appwrite project ID using the query parameter <code>project</code>.</p>
<div class="ide margin-bottom" data-lang="html" data-lang-label="HTML">
    <pre class="line-numbers"><code class="prism language-html" data-prism><?php echo htmlentities('<img src="https://cloud.appwrite.io/v1/storage/buckets/[BUCKET_ID]/files/[FILE_ID]/preview?project=[PROJECT_ID]">'); ?></code></pre>
</div>

<h2><a href="#client-auth" id="client-auth">Client Authentication</a></h2>
<p>You can create account sessions with POST requests to the <a href="/docs/client/account">Account API</a>. Sessions are persisted using secured cookies. You can learn more about session persistence in the <a href="/docs/authentication#persistence">Authentication Guide</a>.</p>
<p>The example below shows creating an account session with the <a href="/docs/client/account#accountCreateEmailSession">Create Account Session with Email</a> endpoint.</p>
<div class="ide margin-bottom" data-lang="http" data-lang-label="HTTP">
    <pre class="line-numbers"><code class="prism language-http" data-prism>POST /v1/account/sessions/email HTTP/1.1
Content-Type: application/json
X-Appwrite-Project: [PROJECT_ID]

{
  "email": "example@email.com",
  "password": "password"
}</code></pre>
</div>

<p> You can find the cookies used to persist the new session in the response headers.</p>
<div class="ide margin-bottom" data-lang="http" data-lang-label="HTTP">
    <pre class="line-numbers"><code class="prism language-http" data-prism>Set-Cookie: a_session_61e71ec784ab035f7259_legacy=eyJ0...aSJ9; expires=Tue, 19-Dec-2023 21:26:51 GMT; path=/; domain=.cloud.appwrite.io; secure; httponly
Set-Cookie: a_session_61e71ec784ab035f7259=eyJ0...aSJ9; expires=Tue, 19-Dec-2023 21:26:51 GMT; path=/; domain=.cloud.appwrite.io; secure; httponly; samesite=None</code></pre>
</div>

<p>These cookies are used in subsequent requests to authenticate the user.</p>
<div class="ide margin-bottom" data-lang="http" data-lang-label="HTTP">
    <pre class="line-numbers"><code class="prism language-http" data-prism>GET /v1/account HTTP/1.1
Cookie: a_session_61e71ec784ab035f7259_legacy=eyJ0...aSJ9; a_session_61e71ec784ab035f7259=eyJ0...aSJ9
Content-Type: application/json
X-Appwrite-Project: [PROJECT_ID]</code></pre>
</div>

<h2><a href="#server-auth" id="server-auth">Server Authentication</a></h2>
<p>Server integrations use API keys to authenticate and are typically used for backend applications.</p>
<p>Server APIs are authenticated with API keys instead of account sessions. Simply pass an <a href="/docs/keys">API key</a> in the <code>X-Appwrite-key: [API-KEY]</code> header with the appropriate scopes.</p>

<div class="ide margin-bottom" data-lang="http" data-lang-label="HTTP">
    <pre class="line-numbers"><code class="prism language-http" data-prism>GET /v1/databases/{databaseId}/collections/{collectionId}/documents HTTP/1.1
Content-Type: application/json
X-Appwrite-Project: [PROJECT_ID]
X-Appwrite-Key: [API_KEY]</code></pre>
</div>

<h2><a href="#server-auth" id="server-auth">JWT Authentication</a></h2>
<p>JWT authentication is frequently used by server applications to act on behalf of a user. Users generate tokens using the <a href="/docs/client/account#accountCreateJWT">Create JWT</a> endpoint. When issuing requests authenticated with a JWT, Appwrite will treat the request like it is from the authenticated user.</p>

<div class="ide margin-bottom" data-lang="http" data-lang-label="HTTP">
    <pre class="line-numbers"><code class="prism language-http" data-prism>GET /v1/account HTTP/1.1
Content-Type: application/json
X-Appwrite-Project: [PROJECT_ID]
X-Appwrite-JWT: [TOKEN]</code></pre>
</div>

<h2><a href="#file-handling" id="file-handling">File Handling</a></h2>
<p>Appwrite implements resumable, chunked uploads for files larger than 5MB. Chunked uploads send files in chunks of 5MB to reduce memory footprint and increase resilience when handling large files. Appwrite SDKs will automatically handle chunked uploads, but it is possible to implement this with the REST API directly.</p>

<p>Upload endpoints in Appwrite, such as <a href="/docs/client/storage#storageCreateFile">Create File</a> and <a href="/docs/server/functions#functionsCreateDeployment">Create Deployment</a>, are different from other endpoints. These endpoints take multipart form data instead of JSON data. To implement chunked uploads, you will need to implement the following headers:</p>

<table class="full text-size-small">
    <thead>
    <tr>
        <td style="width: 280px">Header</td>
        <td style="width: 80px"></td>
        <td>Description</td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td><code>X-Appwrite-Project: [PROJECT-ID]</code></td>
        <td><span class="tag red">required</span></td>
        <td>Contains the ID of your Appwrite Project to the REST API.</td>
    </tr>
    <tr>
        <td><code>Content-Type: multipart/form-data; boundary=[FORM-BOUNDARY]</code></td>
        <td><span class="tag red">required</span></td>
        <td>Contains the content type of the HTTP request and provides a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST">boundary</a> that is used to parse the form data.</td>
    </tr>
    <tr>
        <td><code>Content-Range: bytes [BYTE-RANGE]</code></td>
        <td><span class="tag red">required</span></td>
        <td>Contains information about which bytes are being transmitted in this chunk, with the format <code>[FIRST-BYTE]-[LAST-BYTE]/[TOTAL-BYTES]</code>.</td>
    </tr>
    <tr>
        <td><code>X-Appwrite-ID: [FILE-ID]</code></td>
        <td><span class="tag red">required</span></td>
        <td>Contains ID of the file this chunk belongs to.</td>
    </tr>
    <tr>
        <td><code>X-Appwrite-Key: [API-KEY]</code></td>
        <td><span class="tag">optional</span></td>
        <td>Used for authentication in server integrations. <b>Do not use API keys in client applications.</b></td>
    </tr>
    </tbody>
</table>

<p>The multipart form data is structured as follows:</p>

<table class="full text-size-small">
    <thead>
    <tr>
        <td style="width: 100px">Key</td>
        <td style="width: 80px"></td>
        <td style="width: 150px">Value</td>
        <td style="width: 100px">File Name</td>
        <td>Description</td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>fileId</td>
        <td><span class="tag">optional</span></td>
        <td><code>[FILE-ID]</code></td>
        <td>n/a</td>
        <td>Contains the file ID of the new file. Only used by file chunks following the first chunk uploaded.</td>
    </tr>
    <tr>
        <td>file</td>
        <td><span class="tag red">required</span></td>
        <td><code>[CHUNK-DATA]</code></td>
        <td><code>[FILE-NAME]</code></td>
        <td>Contains file chunk data.</td>
    </tr>
    <tr>
        <td>permissions</td>
        <td><span class="tag red">required</span></td>
        <td><code>[PERMISSION ARRAY]</code></td>
        <td>n/a</td>
        <td>Contains an array of permission strings about who can access the new file.</td>
    </tr>
    </tbody>
</table>

<p>While cURL and fetch are great tools to explore other REST endpoints, it's impractical to use for chunked file uploads because you need to split files into chunks.</p>

<p>The multipart form data posted to file upload endpoints have the following format:</p>

<div class="ide margin-bottom" data-lang="http" data-lang-label="HTTP">
    <pre class="line-numbers"><code class="prism language-http" data-prism>POST /v1/storage/buckets/default/files HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarye0m6iNBQNHlzTpVM
X-Appwrite-Project: demo-project
Content-Range: bytes 10485760-12582912/12582912
X-Appwrite-ID: 6369b0bc1dcf4ff59051

------WebKitFormBoundarye0m6iNBQNHlzTpVM
Content-Disposition: form-data; name="fileId"

unique()
------WebKitFormBoundarye0m6iNBQNHlzTpVM
Content-Disposition: form-data; name="file"; filename="file.txt"
Content-Type: application/octet-stream

[CHUNKED-DATA]
------WebKitFormBoundarye0m6iNBQNHlzTpVM
Content-Disposition: form-data; name="permissions[]"

read("user:627a958ded6424a98a9f")
------WebKitFormBoundarye0m6iNBQNHlzTpVM--</code></pre>
</div>

<h2><a href="#permissions" id="permissions">Permissions</a></h2>

<p>Appwrite SDKs have helpers to generate permission strings, but when using Appwrite without SDKs, you'd need to create the strings yourself.</p>

<h3>Permission Types</h3>

<table class="full text-size-small">
    <thead>
    <tr>
        <td style="width: 250px">SDK</td>
        <td>Permission String</td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td><code>Permission.read()</code></td>
        <td><code>read("[PERMISSION_ROLE]")</code></td>
    </tr>
    <tr>
        <td><code>Permission.create()</code></td>
        <td><code>create("[PERMISSION_ROLE]")</code></td>
    </tr>
    <tr>
        <td><code>Permission.update()</code></td>
        <td><code>update("[PERMISSION_ROLE]")</code></td>
    </tr>
    <tr>
        <td><code>Permission.delete()</td>
        <td><code>delete("[PERMISSION_ROLE]")</code></td>
    </tr>
    <tr>
        <td><code>Permission.write()</td>
        <td><code>write("[PERMISSION_ROLE]")</code></td>
    </tr>
    </tbody>
</table>

<h3>Permission Roles</h3>

<table class="full text-size-small">
    <thead>
    <tr>
        <td style="width: 250px">SDK</td>
        <td>Role String</td>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td><code>Role.any()</code></td>
        <td><code>any</code></td>
    </tr>
    <tr>
        <td><code>Role.guests()</code></td>
        <td><code>guests</code></td>
    </tr>
    <tr>
        <td><code>Role.users()</code></td>
        <td><code>users</code></td>
    </tr>
    <tr>
        <td><code>Role.users([STATUS])</code></td>
        <td><code>users/[STATUS]</code></td>
    </tr>
    <tr>
        <td><code>Role.user([USER_ID])</td>
        <td><code>user:[USER_ID]</code></td>
    </tr>
    <tr>
        <td><code>Role.user([USER_ID], [STATUS])</td>
        <td><code>user:[USER_ID]/[STATUS]</code></td>
    </tr>
    <tr>
        <td><code>Role.team([TEAM_ID])</td>
        <td><code>team:[TEAM_ID]</code></td>
    </tr>
    <tr>
        <td><code>Role.team([TEAM_ID], [ROLE])</td>
        <td><code>team:[TEAM_ID]/[ROLE]</code></td>
    </tr>
    <tr>
        <td><code>Role.member([MEMBERSHIP_ID])</td>
        <td><code>member:[MEMBERSHIP_ID]</code></td>
    </tr>
    </tbody>
</table>

<p>
    <li class="margin-bottom"><a href="/docs/permissions" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Learn more about permissions</a></li>
</p>

<h2><a href="#unique-id" id="unique-id">Unique ID</a></h2>

<p>
Appwrite's SDKs have a helper <code>ID.unique()</code> to generate unique IDs. When using Appwrite without an SDK, pass the string <code>"unique()"</code> into the ID parameter.
</p>

<h2><a href="#query" id="query">Query Methods</a></h2>

<p>
Appwrite's SDKs provide a <code>Query</code> class to generate query strings. When using Appwrite without an SDK, you can template your own strings with the format below.
</p>

<p>Query strings are passed to Appwrite using the <code>queries</code> parameter. You can attach multiple query strings by including the array parameter multiple times in the query string: <code>queries[]="..."&queries[]="..."</code></p>

<table cellspacing="0" cellpadding="0" border="0" class="full margin-bottom-large">
    <thead>
        <tr>
            <th style="width: 150px">Query Method</th>
            <th>Query String</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>select</td>
            <td><code>select([attribute])</code></td>
        </tr>
        <tr>
            <td>equal</td>
            <td><code>equal("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>notEqual</td>
            <td><code>notEqual("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>lessThan</td>
            <td><code>lessThan("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>lessThanEqual</td>
            <td><code>lessThanEqual("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>greaterThan</td>
            <td><code>greaterThan("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>greaterThanEqual</td>
            <td><code>greaterThanEqual("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>between</td>
            <td><code>between("attribute", lowerBound, upperBound)</code></td>
        </tr>
        <tr>
            <td>isNull</td>
            <td><code>isNull("attribute")</code></td>
        </tr>
        <tr>
            <td>isNotNull</td>
            <td><code>isNotNull("attribute")</code></td>
        </tr>
        <tr>
            <td>startsWith</td>
            <td><code>startsWith("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>endsWith</td>
            <td><code>endsWith("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>search</td>
            <td><code>search("attribute", [value])</code></td>
        </tr>
        <tr>
            <td>orderDesc</td>
            <td><code>orderDesc("attribute")</code></td>
        </tr>
        <tr>
            <td>orderAsc</td>
            <td><code>orderAsc("attribute")</code></td>
        </tr>
        <tr>
            <td>cursorAfter</td>
            <td><code>cursorAfter("documentId")</code></td>
        </tr>
        <tr>
            <td>cursorBefore</td>
            <td><code>cursorBefore("documentId")</code></td>
        </tr>
        <tr>
            <td>limit</td>
            <td><code>limit(0)</code></td>
        </tr>
        <tr>
        <td>offset</td>
            <td><code>offset(0)</code></td>
        </tr>
    </tbody>
</table>

<div class="notice margin-bottom">
    <h3>Best Practice</h3>
    <p>When using greater than, greater than or equal to, less than, or less than or equal to, it is not recommended to pass in multiple values. While the API will accept multiple values and return results with <b>or logic</b>, it's best practice to pass in only one value for performance reasons.</p>
</div>
<h2><a href="#rate-limits" id="rate-limits">Rate Limits</a></h2>
<p>Appwrite's REST APIs are protected by the same rate limit policies, just like when using SDKs. Each API has a different rate limit, which is documented in the <b>References</b> section of each service in the Appwrite documentation.</p>

<p>
    <li class="margin-bottom"><a href="https://appwrite.io/docs/rate-limits" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Learn more about Rate Limits</a></li>
</p>

<h2><a href="#stateless" id="stateless">Stateless</a></h2>
<p>Appwrite's REST APIs are stateless. Each time you make a request, Appwrite receives all the information it needs to perform the action, regardless of what requests you make before and after that request.</p>

<p>Since each requests is stateless, they can be handled in any given order when sent concurrently, as long as they don't make conflicting changes to the same resource.</p>

<h2><a href="#specs" id="specs">OpenAPI and Swagger Specs</a></h2>

<p>
Appwrite provides a full REST API specification in the OpenAPI 3 and Swagger 2 formats every release. These can be accessed through Appwrite's GitHub repository and rendered using a variety of parsers and tools.
</p>
<p>
    <li class="margin-bottom"><a href="https://github.com/appwrite/appwrite/tree/master/app/config/specs" target="_blank" rel="noopener"><i class="icon-angle-circled-right margin-start-negative-tiny margin-end-tiny"></i> Find the REST API specification for your Appwrite version</a></li>
</p>
