
Last updated: May 28, 2026
TL;DR: The Quire OAuth 2.0 API lets your app act on a user's Quire account without storing their password. You'll need a registered OAuth app (client ID, secret, redirect URL), a four-step authorization flow (configure, redirect, handle response, exchange code for token), and a refresh-token loop because access tokens expire after one hour.
Building an app that reads or modifies a user's Quire data starts with one decision: how do you authenticate without ever seeing the user's password? The Quire API answers that with OAuth 2.0. This post walks through the OAuth app setup, the four-step authorization flow, the access-token-and-refresh loop, and the common ways developers get this wrong on the first build.
The 1.0.0 release of Quire's OpenAPI shipped in October 2019 and has remained stable. With OAuth, users authorize your app to access their Quire content (tasks, projects, comments, assignments, tags, due dates) and you never touch their credentials. The user can revoke that access at any time without changing their password, which is why password-storing integrations were a security antipattern before OAuth made them unnecessary.
Before you wire up the OAuth API, make sure the API is actually the right tool. Quire has four supported integration surfaces in 2026, and they solve different problems.
| If you need to... | Use | Why |
|---|---|---|
| Read or modify Quire data on a user's behalf in your own app | Quire OAuth API (this post) | User authorization, full CRUD access, no polling required |
| React to events in Quire (task created, status changed, comment added) | Quire Webhooks | Push-based, no polling overhead, lightweight setup |
| Connect Quire to a Claude or other AI agent | Quire MCP server | Standard protocol for LLM tools, ships with a prebuilt server |
| Pull Quire into an automation or BI workflow | OAuth API plus n8n or Power BI | Established pipelines, less custom code |
If you're building a typical app integration (mobile app, web dashboard, sync service), the OAuth API is what you want. The rest of this post assumes you're on that path.
In order to use Quire API, you’ll need to create an Oauth app.
You’ll need to be logged in to your Quire account to create an app.
Go to the Quire developer app console and click on the Create new app button.

Choose the Quire Organization that your app belongs to, the organization members can view/edit all apps belongs to the selected organization.

Give your application a name and Redirect URL, we will discuss the role of the Redirect URL later. For now you can supply the following URL:
http://localhost:3000/callback
Click the Create new app button, your newly created OAuth application will be presented on the developer console page, allowing you to further configure it.

In summary, you should have these three bits of information:
http://localhost:3000/callback
Host your application configuration information in you app.
const clientId = ':wJoMEodI4fSSR54pfNwIuIzLnaJ';
const clientSecret = 'eb8faf4nyd1wbeconw060e9ejui8zg6w8p1hyoex';
const redirectURI = 'http://localhost:3000/callback';
const authorizationUrl = 'https://quire.io/oauth';
const tokenUrl = 'https://quire.io/oauth/token';
const apiUrl = 'https://quire.io/api';Generate an authorization url that you will redirect your users to Quire’s OAuth endpoint URI. This will show a web page where logged in Quire users can authorize your application to access their content.
Sample URL:
https://quire.io/oauth?client_id=your-client-ID&redirect_uri=your-redirect-uriAn authorization link view example might look like:
var http = require('http');
var url = require('url');
var server = http.createServer(function (req, res) {
var uri = url.parse(req.url, true);
if (uri.pathname == '/') {
//..
} else if (uri.pathname == "/install") {
var authUrl = authorizationUrl
+ '?client_id=' + clientId
+ '&redirect_uri=' + encodeURIComponent(redirectURI);
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(
'<html><body>'
+ '<a href="' + authUrl + '">Connect Quire</a>'
+ '</body></html>');
res.end();
} else if (uri.pathname == "/callback") {
//...
}
});
server.listen(3000);The state parameter is a random string used to prevent Cross-Site Request Forgery (CSRF) attacks. You should randomly generate a character string. It will be passed back to your app, unchanged, in Step 3. Your application should validate this value. Though it is optional, we strongly recommend including this parameter.
Sample URL:
https://quire.io/oauth?client_id=your-client-ID&redirect_uri=your-redirect-uri&state=lpcl9v94zThe OAuth 2.0 server responds to your application's access request by using the URL specified in the redirect_uri.
If the user approves the access request, then the response contains an authorization code. If the user does not approve the request, the response contains an error message. The authorization code or error message that is returned to the web server appears on the query string, as shown below:
An error response:
http://localhost:3000/callback?error=access_deniedAn authorization code response:
http://localhost:3000/callback?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7A callback example might look like:
//...
} else if (uri.pathname == "/callback") {
var result = uri.query;
var message = 'Auth fail';
if (result["error_description"] != null) {
message = result["error_description"];
if (result["error"] == 'access_denied') {
//display reject message
}
messageView(res, message);
} else if (result["code"] != null) {
return exchangeAccessToken(result["code"])
.then(function(data) {
var token = data['access_token'];
message = token != null ? 'Success': 'Fail';
messageView(res, message);
});
}
}When the user is redirected back to your application redirect_uri, a code and state parameter will also be present in the querystring parameters. The state is your CSRF anti-forgery token to validate the request.
Extract the code and state from the query string parameters. The state may be validated at this point.
A validate example might look like:
} else if (result["code"] != null) {
if (result["state"] != stateFromSession(res))
return messageView(res, 'invalid state');
return exchangeAccessToken(result["code"])
.then(function(data) {
var token = data['access_token'];
message = token != null ? 'Success': 'Fail';
messageView(res, message);
});
}Your application needs to make a POST call to the token endpoint with the extracted authorization code and the request parameters in the below.
| Parameter | Value |
|---|---|
| grant_type | authorization_code |
| code | {your-authorization-code} |
| client_id | {your-client-ID} |
| client_secret | {your-client-secret} |
| redirect_uri | Required if you specified redirect_uri in Step 2. The value must be identical to the one used there. |
A request an access token example might look like this:
var request = require('request');
function exchangeAccessToken(code) {
return new Promise(function(resolve, reject){
request.post({
url: tokenUrl,
form: {
grant_type: 'authorization_code',
code: code,
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectURI
}
},
function (error, httpResponse, body) {
if (error) {
return reject(error);
}
resolve(JSON.parse(body))
});
});
}The access token you receive in response will be a JSON format.
Example Response:
{
"access_token":"ACCESS_TOKEN",
"token_type": "bearer",
"expires_in":2592000,
"refresh_token":"REFRESH_TOKEN"
}The token should be kept carefully and permanently since you need it to access every Quire API.
Your app now has an access token that it can be used to make API calls on user's behalf.
Make the API call passing the access token as a bearer token in the header of the request.
An api call example might look like:
function getCurrentUser(token) {
return new Promise(function(resolve, reject){
request.get({
url: apiUrl + '/user/id/me',
headers: {
"Authorization": "Bearer " + token
}
},
function (error, httpResponse, body) {
if (error) {
return reject(error);
}
resolve(JSON.parse(body))
});
});
}Example Response:
{
"email": "john@gmail.cc",
"website": "https://coolwebsites.com",
"id": "My_ID",
"description": "This is *cool*!",
"url": "https://quire.io/u/My_ID",
"nameText": "My Name",
"nameHtml": "My Name",
"descriptionText": "This is cool!",
"descriptionHtml": "This is <i>cool</i>!",
"image": "https://quire.s3.amazonaws.com/oid/image.jpg",
"iconColor": "37",
"name": "My Name",
"oid": "Dyh2YkFcu9uLgLFIeN1kB4Ld"
}An access token intentionally is meant for short-term use only. This is an important security mechanism of OAuth 2.0. When using the Authorization Code Grant Flow, the access tokens have an one-hour lifetime by default.
When an access token expires, an HTTP 401 error will be returned:
{
code: 401,
message: 'Invalid or expired token.'
}
Your application will need to refresh the access token.
function refreshToken(refreshToken) {
return new Promise(function(resolve, reject){
request.post({
url: tokenUrl,
form: {
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: clientId,
client_secret: clientSecret
}
},
function (error, httpResponse, body) {
if (error) {
return reject(error);
}
resolve(JSON.parse(body))
});
});
}Alternatively, your application could redirect the user to the authentication flow.
After watching teams build against the Quire API, the same five problems recur. None are subtle; all are avoidable.
1. Storing the client secret in client-side code. The client secret is exactly that, a secret. If it ends up in a mobile app binary or a browser bundle, anyone can extract it and impersonate your app. The secret should live only on a server you control. If your app is purely client-side (single-page app, mobile), use the OAuth 2.0 PKCE flow instead of embedding the secret.
2. Forgetting to validate the state parameter. The state parameter exists to prevent CSRF attacks. If you skip validating it on the callback, you've left a hole that a malicious site can use to silently associate a Quire account with an attacker's app session. Generate state per-request, store it in the session, and reject any callback where the returned state doesn't match.
3. Treating the access token as permanent. Access tokens expire after one hour. Apps that store the token and never refresh it work fine on Day 1 and break silently on Day 2. Build the refresh-token loop before you ship anything, not as a "fix it when users complain" patch.
4. Polling the API for changes. Polling burns rate limit, latency, and battery. If you need to react to changes in Quire, use webhooks instead. The webhook ships the event to you instantly; the API call you would have made to detect that event never has to happen.
5. Skipping the redirect URL allowlist. Every OAuth app registration accepts a list of redirect URLs. If you wildcard it or skip configuring it precisely, an attacker can register their callback as your redirect target and intercept authorization codes. Add only the exact URLs your app actually uses.
If you're building against the API for the first time, the fastest way to learn the patterns is to fork an existing integration like the n8n connector and study how it handles auth, refresh, and error states.
Three patterns where you should reach for a different integration surface instead.
If none of those apply, the OAuth API is your tool.
No. The Quire API is available on every plan including the free tier. Rate limits apply on all plans; review the API documentation for the current per-app limits.
The API is HTTP-based with JSON request and response bodies, so any language with an HTTP client and JSON parser works. The examples in this post are JavaScript, but the same flow works identically in Python, Go, Ruby, PHP, and any other language. There's no official client library; the API surface is small enough that a thin wrapper around your language's HTTP client is the common pattern.
Access tokens expire after one hour by default. Refresh tokens last longer and can be used to obtain new access tokens without re-prompting the user. Build the refresh-token loop into your app from Day 1.
Yes. OAuth scopes let you request only the permissions your app needs. If your app only reads tasks, request read scopes. Quire users will see the requested scopes on the authorization page and can decline if the request feels excessive, so over-requesting hurts conversion.
The API is a general-purpose REST surface for any app that wants to read or modify Quire data on behalf of a user. The MCP server is purpose-built for Claude and other LLM agents: it handles auth, exposes a standard tool schema, and means you don't write OAuth code yourself. Use the API for traditional app integrations; use MCP for LLM agent integrations.
That's the full OAuth 2.0 flow for the Quire API: OAuth app setup, four-step authorization, access token usage, and the refresh loop. The most common reason developers ship a working integration that breaks two days later is treating the refresh loop as a later fix instead of building it in from Day 1.
Get started at the Quire developer app console or read the full API reference. If you'd rather not write OAuth code at all, the Quire MCP server handles authentication for you and is the right choice for LLM agent integrations. For event-driven integrations, webhooks are usually a better fit than the polling pattern most first-time API integrations end up with.