LogoRecal

Quick Start

Get started with Recal

Warning: If you want to send requests directly from the website instead of your own client, use our more robust Swagger Routes.

Your First API Call

1. Create an Account & Get API Key

Sign up and log in to your Recal dashboard. Go to "API Keys" in the sidebar, create a new key, and copy it somewhere safe.

2. Connect Your OAuth Providers

In the dashboard, go to "Settings" → "OAuth", enter your OAuth Client ID/Secret, and save.

How do I get OAuth credentials?

3. Make Your First API Call

You can make requests directly from the docs. Click on headers below and just make sure to keep Bearer in your input.

GET
/status

Authorization

AuthorizationRequiredBearer <token>

The Recal API token

In: header

Response Body

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring
curl -X GET "https://api.recal.dev/status" \
  -H "Authorization: Bearer <token>"
fetch("https://api.recal.dev/status", {
  headers: {
    "Authorization": "Bearer <token>"
  }
})
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
  url := "https://api.recal.dev/status"

  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Add("Authorization", "Bearer <token>")
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://api.recal.dev/status"

response = requests.request("GET", url, headers = {
  "Authorization": "Bearer <token>"
})

print(response.text)
"API is running and healthy! 🦊"

Connect Your First Calendar

1. Create a User

Give them any ID, e.g., to match your existing users. If you don't have an org yet, remove it from the body.

POST
/v1/users

Authorization

AuthorizationRequiredBearer <token>

The Recal API token

In: header

Request Body

application/jsonRequired
idRequiredstring
organizationSlugsarray<string>

Response Body

TypeScript Definitions

Use the response body type in TypeScript.

idRequiredstring
createdAtRequiredDate | string | number

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring
curl -X POST "https://api.recal.dev/v1/users" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "string",
    "organizationSlugs": [
      "string"
    ]
  }'
const body = JSON.stringify({
  "id": "string",
  "organizationSlugs": [
    "string"
  ]
})

fetch("https://api.recal.dev/v1/users", {
  headers: {
    "Authorization": "Bearer <token>"
  },
  body
})
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "strings"
)

func main() {
  url := "https://api.recal.dev/v1/users"
  body := strings.NewReader(`{
    "id": "string",
    "organizationSlugs": [
      "string"
    ]
  }`)
  req, _ := http.NewRequest("POST", url, body)
  req.Header.Add("Authorization", "Bearer <token>")
  req.Header.Add("Content-Type", "application/json")
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://api.recal.dev/v1/users"
body = {
  "id": "string",
  "organizationSlugs": [
    "string"
  ]
}
response = requests.request("POST", url, json = body, headers = {
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
})

print(response.text)
{
  "id": "user_1234567890",
  "createdAt": "2025-05-03T13:50:25.000Z"
}
"Organization not found"
"User already exists"

Already have existing connections? If you've already obtained OAuth tokens elsewhere (e.g., through Clerk or another OAuth flow), you can skip the authorization link and directly create the connection using the tokens you already have via the Create OAuth Connection endpoint.

Generate an OAuth authorization link for your user to connect their calendar provider.

Replace {userId} with your actual user ID and {provider} with either google or microsoft. You can add query params like accessType=offline (to get refresh tokens) or redirectUrl (to override the default redirect).

GET
/v1/users/{userId}/oauth/{provider}/link

Authorization

AuthorizationRequiredBearer <token>

The Recal API token

In: header

Path Parameters

userIdRequiredstring
providerRequiredstring

Calendar provider (google or microsoft)

Pattern: "^(google|microsoft)$"

Query Parameters

scopestring
accessTypestring
redirectUrlstring
Format: "url"

Response Body

TypeScript Definitions

Use the response body type in TypeScript.

urlRequiredstring
Format: "url"

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring
curl -X GET "https://api.recal.dev/v1/users/string/oauth/string/link?scope=edit&accessType=offline&redirectUrl=string" \
  -H "Authorization: Bearer <token>"
fetch("https://api.recal.dev/v1/users/string/oauth/string/link?scope=edit&accessType=offline&redirectUrl=string", {
  headers: {
    "Authorization": "Bearer <token>"
  }
})
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
  url := "https://api.recal.dev/v1/users/string/oauth/string/link?scope=edit&accessType=offline&redirectUrl=string"

  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Add("Authorization", "Bearer <token>")
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://api.recal.dev/v1/users/string/oauth/string/link?scope=edit&accessType=offline&redirectUrl=string"

response = requests.request("GET", url, headers = {
  "Authorization": "Bearer <token>"
})

print(response.text)
{
  "url": "https://example.com"
}
"User not found"

3. Verify OAuth Connection

After the user completes the OAuth flow and returns to your callback URL, verify the connection:

POST
/v1/users/oauth/{provider}/verify

Authorization

AuthorizationRequiredBearer <token>

The Recal API token

In: header

Request Body

application/jsonRequired
codeRequiredstring
scopeRequiredarray<string>
stateRequiredstring

Path Parameters

providerRequiredstring

Calendar provider (google or microsoft)

Pattern: "^(google|microsoft)$"

Query Parameters

redirectUrlstring

Response Body

TypeScript Definitions

Use the response body type in TypeScript.

successRequiredboolean

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring
curl -X POST "https://api.recal.dev/v1/users/oauth/string/verify?redirectUrl=string" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "string",
    "scope": [
      "string"
    ],
    "state": "string"
  }'
const body = JSON.stringify({
  "code": "string",
  "scope": [
    "string"
  ],
  "state": "string"
})

fetch("https://api.recal.dev/v1/users/oauth/string/verify?redirectUrl=string", {
  headers: {
    "Authorization": "Bearer <token>"
  },
  body
})
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
  "strings"
)

func main() {
  url := "https://api.recal.dev/v1/users/oauth/string/verify?redirectUrl=string"
  body := strings.NewReader(`{
    "code": "string",
    "scope": [
      "string"
    ],
    "state": "string"
  }`)
  req, _ := http.NewRequest("POST", url, body)
  req.Header.Add("Authorization", "Bearer <token>")
  req.Header.Add("Content-Type", "application/json")
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://api.recal.dev/v1/users/oauth/string/verify?redirectUrl=string"
body = {
  "code": "string",
  "scope": [
    "string"
  ],
  "state": "string"
}
response = requests.request("POST", url, json = body, headers = {
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
})

print(response.text)
{
  "success": true
}
"Invalid code"
"OAuth authorization provider not found"

5. List Calendar Events

Now you can fetch the user's calendar events:

GET
/v1/users/{userId}/calendar/events

Authorization

AuthorizationRequiredBearer <token>

The Recal API token

In: header

Path Parameters

userIdRequiredstring

Query Parameters

minDateRequiredDate | string | number
maxDateRequiredDate | string | number
timeZonestring
Default: "UTC"
providerstring | array<unknown>

Response Body

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredarray<object>

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring

TypeScript Definitions

Use the response body type in TypeScript.

responseRequiredstring
curl -X GET "https://api.recal.dev/v1/users/string/calendar/events?minDate=%5Bobject+Object%5D&maxDate=%5Bobject+Object%5D&timeZone=UTC&provider=google" \
  -H "Authorization: Bearer <token>"
fetch("https://api.recal.dev/v1/users/string/calendar/events?minDate=%5Bobject+Object%5D&maxDate=%5Bobject+Object%5D&timeZone=UTC&provider=google", {
  headers: {
    "Authorization": "Bearer <token>"
  }
})
package main

import (
  "fmt"
  "net/http"
  "io/ioutil"
)

func main() {
  url := "https://api.recal.dev/v1/users/string/calendar/events?minDate=%5Bobject+Object%5D&maxDate=%5Bobject+Object%5D&timeZone=UTC&provider=google"

  req, _ := http.NewRequest("GET", url, nil)
  req.Header.Add("Authorization", "Bearer <token>")
  res, _ := http.DefaultClient.Do(req)
  defer res.Body.Close()
  body, _ := ioutil.ReadAll(res.Body)

  fmt.Println(res)
  fmt.Println(string(body))
}
import requests

url = "https://api.recal.dev/v1/users/string/calendar/events?minDate=%5Bobject+Object%5D&maxDate=%5Bobject+Object%5D&timeZone=UTC&provider=google"

response = requests.request("GET", url, headers = {
  "Authorization": "Bearer <token>"
})

print(response.text)
[
  {
    "id": "string",
    "metaId": "string",
    "subject": "string",
    "description": "string",
    "start": "1970-01-01T00:00:00.000Z",
    "end": "1970-01-01T00:00:00.000Z",
    "location": "string",
    "attendees": [
      {
        "email": "string",
        "responseStatus": "needsAction"
      }
    ],
    "original": null
  }
]
"User has not the needed calendars connected"
"Organization not found"

Next Steps

Need Help?