> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://catena.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://catena.ferndocs.com/_mcp/server.

# Create Tms Invitation

POST https://api.catenatelematics.com/v2/orgs/tms-invitations
Content-Type: application/json

Create an invitation with a magic link to onboard a TMS account.

Returns an invitation object with a unique ID that serves as the magic link token.
Share this link with the TMS account to begin the onboarding process.
Once accepted, a TMS share agreement is automatically created to enable data sharing.

**Note:** If an active, accepted, or declined invitation already exists for the same `tms_account_ref`,
the invitation is refreshed (expiration extended, reactivated if declined) and returned with status
code 200 instead of 201.

Reference: https://catena.ferndocs.com/api-reference/orgs/tms-invitations/create-tms-invitation

## Authentication

- `Authorization` header (bearer token, required) — Bearer authentication of the form `Bearer <token>`, where token is your auth token.

## Request

### Body (application/json)

This endpoint expects a TmsInvitationCreate.

- `tms_account_ref` (string, required) — Your internal identifier for the TMS account. Use this to map Catena TMS accounts back to your system. This value will be returned in webhooks and redirect URLs.
- `limit_tms` (list of string, optional, nullable) — Restrict which transportation management systems the TMS account can connect to. Useful if you only need data from specific TMSs. If omitted, all available TMSs are shown. Cannot be combined with `exclude_tms`.
- `exclude_tms` (list of string, optional, nullable) — Exclude specific transportation management systems from the ones shown to the TMS account. Useful if you want the TMS account to have access to every TMS except a few. Cannot be combined with `limit_tms`.
- `permissions` (map from string to enum, optional, nullable) — Defines which TMS-sourced resources (loads, shipments, carriers, etc.) the TMS account must grant access to and the permission level for each. If omitted, we will request READ permissions for all resources.
  - Allowed values: `read`, `read_write`
- `expires_in_hours` (integer, optional, default: 24) — How long the invitation link remains valid (1-672 hours). Default is 24 hours.

## Response

### 200

Existing invitation refreshed (already exists for this tms_account_ref)

- `id` (string, required) — Unique invitation identifier
- `created_at` (datetime, required) — When the invitation was created
- `magic_link` (string, required) — The magic link used to accept the invitation. Share this URL with the TMS account to begin onboarding.
- `expires_at` (datetime, required) — The expiration date and time of the invitation
- `expires_in_hours` (integer, required) — The number of hours the invitation is valid for.
- `status` (enum, required) — The current status of the invitation (active, accepted, declined, expired, or deleted)
  - Allowed values: `active`, `accepted`, `declined`, `expired`, `deleted`
- `tms_account_ref` (string, required) — Your internal identifier for the TMS account. Use this to map Catena TMS accounts back to your system.
- `partner_id` (string, required) — Your organization ID requesting access to TMS account data
- `partner_slug` (string, required, nullable) — The slug of your organization
- `accepted_at` (datetime, required, nullable) — The date and time the invitation was accepted. Null if not accepted.
- `accepted_by` (string, required, nullable) — Identifier of who accepted the invitation on behalf of the TMS account. Null if not accepted.
- `pre_registration_access_token` (string, required, nullable) — The JWT token used to pre-register the TMS account. It allows the TMS account to create connections after accepting the invitation even without registering a user. Value is set after the invitation is accepted.
- `pre_registration_refresh_token` (string, required, nullable) — The refresh token used to pre-register the TMS account. It allows the TMS account to create connections after accepting the invitation even without registering a user. Value is set after the invitation is accepted.
- `decline_reason` (string, required, nullable) — The reason provided for declining the invitation. Null if invitation was not declined.
- `declined_at` (datetime, required, nullable) — The date the invitation was declined. Null if invitation was not declined.
- `limit_tms` (list of string, required, nullable) — Restrict which transportation management systems the TMS account can connect to.
- `exclude_tms` (list of string, required, nullable) — Exclude specific transportation management systems from the ones shown to the TMS account.
- `permissions` (map from string to enum, required) — Defines which TMS-sourced resources (loads, shipments, carriers, etc.) the TMS account must grant access to and the permission level for each.
  - Allowed values: `read`, `read_write`
- `tms_account_id` (string, optional, nullable) — The Catena TMS account ID created after accepting the invitation. Null if invitation has not been accepted.

### 201

New invitation created

- `id` (string, required) — Unique invitation identifier
- `created_at` (datetime, required) — When the invitation was created
- `magic_link` (string, required) — The magic link used to accept the invitation. Share this URL with the TMS account to begin onboarding.
- `expires_at` (datetime, required) — The expiration date and time of the invitation
- `expires_in_hours` (integer, required) — The number of hours the invitation is valid for.
- `status` (enum, required) — The current status of the invitation (active, accepted, declined, expired, or deleted)
  - Allowed values: `active`, `accepted`, `declined`, `expired`, `deleted`
- `tms_account_ref` (string, required) — Your internal identifier for the TMS account. Use this to map Catena TMS accounts back to your system.
- `partner_id` (string, required) — Your organization ID requesting access to TMS account data
- `partner_slug` (string, required, nullable) — The slug of your organization
- `accepted_at` (datetime, required, nullable) — The date and time the invitation was accepted. Null if not accepted.
- `accepted_by` (string, required, nullable) — Identifier of who accepted the invitation on behalf of the TMS account. Null if not accepted.
- `pre_registration_access_token` (string, required, nullable) — The JWT token used to pre-register the TMS account. It allows the TMS account to create connections after accepting the invitation even without registering a user. Value is set after the invitation is accepted.
- `pre_registration_refresh_token` (string, required, nullable) — The refresh token used to pre-register the TMS account. It allows the TMS account to create connections after accepting the invitation even without registering a user. Value is set after the invitation is accepted.
- `decline_reason` (string, required, nullable) — The reason provided for declining the invitation. Null if invitation was not declined.
- `declined_at` (datetime, required, nullable) — The date the invitation was declined. Null if invitation was not declined.
- `limit_tms` (list of string, required, nullable) — Restrict which transportation management systems the TMS account can connect to.
- `exclude_tms` (list of string, required, nullable) — Exclude specific transportation management systems from the ones shown to the TMS account.
- `permissions` (map from string to enum, required) — Defines which TMS-sourced resources (loads, shipments, carriers, etc.) the TMS account must grant access to and the permission level for each.
  - Allowed values: `read`, `read_write`
- `tms_account_id` (string, optional, nullable) — The Catena TMS account ID created after accepting the invitation. Null if invitation has not been accepted.

## Errors

### 400 Bad Request Error

Bad Request

- `code` (integer, optional, default: 400)
- `message` (string, optional, default: Bad Request)
- `detail` (string, optional, nullable)

### 401 Unauthorized Error

Unauthorized

- `code` (integer, optional, default: 401)
- `message` (string, optional, default: Unauthorized)
- `detail` (string, optional, nullable)

### 403 Forbidden Error

Forbidden

- `code` (integer, optional, default: 403)
- `message` (string, optional, default: Forbidden)
- `detail` (string, optional, nullable)

### 404 Not Found Error

Not Found

- `code` (integer, optional, default: 404)
- `message` (string, optional, default: Not Found)
- `detail` (string, optional, nullable)

### 405 Method Not Allowed Error

Method Not Allowed

- `code` (integer, optional, default: 405)
- `message` (string, optional, default: Method Not Allowed)
- `detail` (string, optional, nullable)

### 409 Conflict Error

Conflict

- `code` (integer, optional, default: 409)
- `message` (string, optional, default: Conflict)
- `detail` (string, optional, nullable)

### 422 Unprocessable Entity Error

Unprocessable Entity

- `code` (integer, optional, default: 422)
- `message` (string, optional, default: Invalid Request Body)
- `detail` (list of ValidationErrorDetail, optional, nullable)

### 429 Too Many Requests Error

Too Many Requests

- `code` (integer, optional, default: 429)
- `message` (string, optional, default: Too Many Requests)
- `detail` (RetryAfterDetail, optional, nullable)

### 500 Internal Server Error

Internal Server Error

- `message` (string, optional, default: Internal Server Error)

## Types

### ValidationErrorDetail

- `path` (string, required)
- `input` (string, required)
- `message` (string, required)
- `error_type` (string, required)

### RetryAfterDetail

- `retry_after_seconds` (integer, required)
- `message` (string, required)

## Examples

### Example 1

**Request**

```json
{
  "tms_account_ref": "string"
}
```

**Response**

```json
{
  "id": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "magic_link": "string",
  "expires_at": "2024-01-15T09:30:00Z",
  "expires_in_hours": 1,
  "status": "active",
  "tms_account_ref": "string",
  "partner_id": "string",
  "partner_slug": "string",
  "accepted_at": "2024-01-15T09:30:00Z",
  "accepted_by": "string",
  "pre_registration_access_token": "string",
  "pre_registration_refresh_token": "string",
  "decline_reason": "string",
  "declined_at": "2024-01-15T09:30:00Z",
  "limit_tms": [
    "string"
  ],
  "exclude_tms": [
    "string"
  ],
  "permissions": {},
  "tms_account_id": "string"
}
```

**SDK Code**

```python
import requests

url = "https://api.catenatelematics.com/v2/orgs/tms-invitations"

payload = { "tms_account_ref": "string" }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.catenatelematics.com/v2/orgs/tms-invitations';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"tms_account_ref":"string"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

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

func main() {

	url := "https://api.catenatelematics.com/v2/orgs/tms-invitations"

	payload := strings.NewReader("{\n  \"tms_account_ref\": \"string\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.catenatelematics.com/v2/orgs/tms-invitations")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"tms_account_ref\": \"string\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.catenatelematics.com/v2/orgs/tms-invitations")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"tms_account_ref\": \"string\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.catenatelematics.com/v2/orgs/tms-invitations', [
  'body' => '{
  "tms_account_ref": "string"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.catenatelematics.com/v2/orgs/tms-invitations");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"tms_account_ref\": \"string\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["tms_account_ref": "string"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.catenatelematics.com/v2/orgs/tms-invitations")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Example 2

**Request**

```json
{
  "tms_account_ref": "string"
}
```

**Response**

```json
{
  "id": "string",
  "created_at": "2024-01-15T09:30:00Z",
  "magic_link": "string",
  "expires_at": "2024-01-15T09:30:00Z",
  "expires_in_hours": 1,
  "status": "active",
  "tms_account_ref": "string",
  "partner_id": "string",
  "partner_slug": "string",
  "accepted_at": "2024-01-15T09:30:00Z",
  "accepted_by": "string",
  "pre_registration_access_token": "string",
  "pre_registration_refresh_token": "string",
  "decline_reason": "string",
  "declined_at": "2024-01-15T09:30:00Z",
  "limit_tms": [
    "string"
  ],
  "exclude_tms": [
    "string"
  ],
  "permissions": {},
  "tms_account_id": "string"
}
```

**SDK Code**

```python
import requests

url = "https://api.catenatelematics.com/v2/orgs/tms-invitations"

payload = { "tms_account_ref": "string" }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.catenatelematics.com/v2/orgs/tms-invitations';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"tms_account_ref":"string"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

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

func main() {

	url := "https://api.catenatelematics.com/v2/orgs/tms-invitations"

	payload := strings.NewReader("{\n  \"tms_account_ref\": \"string\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.catenatelematics.com/v2/orgs/tms-invitations")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"tms_account_ref\": \"string\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.catenatelematics.com/v2/orgs/tms-invitations")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"tms_account_ref\": \"string\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.catenatelematics.com/v2/orgs/tms-invitations', [
  'body' => '{
  "tms_account_ref": "string"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.catenatelematics.com/v2/orgs/tms-invitations");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"tms_account_ref\": \"string\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["tms_account_ref": "string"] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.catenatelematics.com/v2/orgs/tms-invitations")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```