Skip to main content
If you build a product your customers use alongside Filed, OAuth lets them connect the two themselves. Your customer approves the connection in Filed, and you receive a token scoped to the one workspace they chose. Nobody emails an API key around, and the customer can disconnect you at any time. This is the flow to use when you are integrating on behalf of many Filed customers. If you are building for a single workspace you control, an API key is simpler. Filed implements the authorization code grant with PKCE. Confidential clients only: every call to the token endpoint is authenticated with your client secret.

Before you start

Filed registers each partner by hand. Contact Filed to receive: You supply your redirect URI during registration. Filed matches it exactly, so a trailing slash or an extra query parameter is rejected. Register every environment you need (production, staging) as a separate URI.
Filed stores only a SHA-256 digest of your client secret. If you lose it, Filed issues a new one rather than recovering the old one, and your integration stops working until you deploy the replacement.
Your customers must already be Filed customers. Filed does not offer self-serve signup, so a workspace is created under contract by the Filed team. Signing a customer up in your product does not create a Filed account for them.

Endpoints

Filed publishes RFC 8414 metadata, so most OAuth libraries can configure themselves:
cURL

Scopes

Request only what you use. Your customer sees this list on the consent screen in the wording below, so an unnecessary scope is a reason for them to decline. Omit the scope parameter to request everything you are registered for.
Scopes are recorded on the grant and shown to the customer, but Filed does not yet enforce them field by field. Today a token’s real limit is the workspace it belongs to, and whether the grant included any write scope at all. Treat scopes as a contract you keep, not a boundary Filed enforces for you.

Connect a customer

1. Send the customer to Filed

Generate a PKCE verifier and challenge, store the verifier against the user’s session, and redirect their browser to the authorization endpoint.
string
required
The identifier Filed issued you.
string
required
Must match one of your registered URIs exactly.
string
required
Always code.
string
required
Base64url SHA-256 of your verifier.
string
required
Always S256. Filed rejects plain.
string
Space-delimited. Omit to request every scope you are registered for.
string
Returned to you unchanged. Use it to defend against CSRF.

2. The customer approves

Filed asks them to sign in, choose which of their workspaces to connect, and review what you are asking for. The grant covers that one workspace: your token cannot reach the customer’s other workspaces. Only an opaque handle for the request travels in the browser. Your client_id and the requested scopes stay on Filed’s server, so nothing the customer can edit in the URL bar changes what the grant permits.

3. Receive the code

Filed redirects back to your redirect_uri:
Check state matches what you stored. The code is single use and expires after 60 seconds, so exchange it immediately.

4. Exchange the code for tokens

Call the token endpoint from your backend. Authenticate with HTTP Basic, sending your client_id as the username and your client_secret as the password. This is the client_secret_basic method.
cURL
string
A Filed workspace token. Send it as a bearer token to the Filed API.
string
Always Bearer.
number
Seconds until the access token expires. Always 180.
string
Store this against the customer. It is valid for 90 days and is the credential that represents the grant.
string
Space-delimited scopes the customer approved. Returned on this exchange only.
string
The workspace the grant is pinned to. Returned on this exchange only, because it never changes for the life of the grant.
string
Display name of that workspace, so you can show the customer what they connected. Returned on this exchange only.

5. Call the Filed API

cURL
See Making requests for the API itself.

Pushing documents

Documents go in the same way as any other integration — stage the files, then call addClientDocuments — with one extra step the first time you push for a client. Link the client to your connection first. addClientDocuments takes a Filed clientId, and the push is refused with FAILED_PRECONDITION until a link exists between your connection and that client:
externalId is your identifier for the client. Filed cannot create the link for you because only you know that value, and it is what lets you find the same client again later. The link is also how Filed attributes what you push. A document filed under your connection’s link is traceable back to your product; without one it would be indistinguishable from a document the customer uploaded in Filed themselves — the access token identifies the customer who approved your grant, not whoever acted in your product.
Only addClientDocuments carries this attribution today. Documents attached through createClient, initiateTaxReview, or initiateTaxAdvisor are recorded as ordinary uploads.

Refreshing

Access tokens last three minutes. This is deliberate: the long-lived credential is the refresh token you hold, and what reaches the API is not. Exchange the refresh token whenever you need a fresh access token rather than caching one.
cURL
The response has the same shape, without scope, workspace_id, and workspace_name. Filed returns the same refresh token rather than rotating it, so there is nothing to store after a refresh. The refresh token is valid for 90 days. When it expires, send the customer through the flow again.
The refresh token grants access to a Filed workspace for as long as it is valid. Encrypt it at rest and never expose it to a browser or a mobile client.

When a customer disconnects

Customers manage the connection in Filed under Plugins, where your integration appears by name alongside their others. Disconnecting revokes the grant. Because access tokens are short lived, a disconnect takes effect within three minutes at the outside. Your next refresh fails with invalid_grant. The same happens if the customer who approved the grant leaves the workspace, or if the 90 days elapse. Treat invalid_grant on refresh as “this connection is over”: stop retrying, clear the stored token, and prompt the customer to reconnect if they want to continue.

Handling failures

Some failures happen before Filed can trust your redirect_uri: an unknown client_id, or a URI that is not registered. Filed renders an error page in the customer’s browser rather than redirecting, because bouncing a browser to an unverified URI is how an open redirect is built. Check your registration if a customer reports seeing this page. Once the redirect URI is verified, everything comes back to you as query parameters, per RFC 6749:
access_denied covers three distinct situations, and the description tells you which: The last two are ordinary outcomes, not rare edge cases, because Filed accounts are provisioned under contract. Filed hands the browser back to you rather than stopping on a Filed page, so your product is the one that explains what to do next. The token endpoint returns JSON errors:

Current limits

Worth knowing before you build:
  • Scopes are not enforced field by field yet, as described above.
  • There is no token revocation endpoint. The discovery document omits it rather than advertising one that does not work. Customers revoke by disconnecting.
  • Refresh tokens are not rotated. Refreshing returns the same token, so its 90-day life is both how long a leaked copy stays usable and how often a customer reconnects.
  • Authorization requests expire after 10 minutes, so a customer who leaves the consent screen open and comes back has to start again.