> This page is for Developers.

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

> Create and authenticate with application tokens for secure M2M communication, including security best practices, IP restrictions, and token expiration handling.

Application tokens are the recommended way to authenticate with the emnify REST API for machine-to-machine (M2M) communications.
Unlike user credentials, application tokens don't require storing passwords on your servers or dealing with multi-factor authentication (MFA).

Key benefits:

* Create multiple tokens for different applications or environments
* Restrict tokens to specific IP address ranges
* Set expiration dates for automatic revocation
* Revoke individual tokens at any time without affecting others

Security best practices:

* Always set an expiration date for production tokens
* Use IP restrictions when your server has a static IP
* Store tokens in environment variables, never in source code
* Rotate tokens periodically and revoke unused tokens

> **Note**
>
> Application tokens are scoped to the Workspace where they were created.
> Two cross-Workspace operations ([Workspace switching](/developers/api/workspaces/authenticate-workspace) and [cross-Workspace SIM transfers](/developers/api/sim/sim-migration-workspace)) temporarily require [user credentials](/developers/auth/user-credentials), but these APIs are being updated to support application tokens.

## Create an application token

To create an `application_token`, send a POST request to `/api/v1/application_token`.

### Request

POST [https://cdn.emnify.net/api/v1/application\_token](https://cdn.emnify.net/api/v1/application_token)

**`Expiry Date`**

```curl Expiry Date
curl -X POST https://cdn.emnify.net/api/v1/application_token \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     -d '{
  "description": "Token with expiry date",
  "expiry_date": "2021-05-29T00:00:00.000Z"
}'
```

**`Expiry Date`**

```python Expiry Date
import requests

url = "https://cdn.emnify.net/api/v1/application_token"

payload = {
    "description": "Token with expiry date",
    "expiry_date": "2021-05-29T00:00:00.000Z"
}
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

**`Expiry Date`**

```javascript Expiry Date
const url = 'https://cdn.emnify.net/api/v1/application_token';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"description":"Token with expiry date","expiry_date":"2021-05-29T00:00:00.000Z"}'
};

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

**`Expiry Date`**

```go Expiry Date
package main

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

func main() {

	url := "https://cdn.emnify.net/api/v1/application_token"

	payload := strings.NewReader("{\n  \"description\": \"Token with expiry date\",\n  \"expiry_date\": \"2021-05-29T00:00:00.000Z\"\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))

}
```

**`Expiry Date`**

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

url = URI("https://cdn.emnify.net/api/v1/application_token")

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  \"description\": \"Token with expiry date\",\n  \"expiry_date\": \"2021-05-29T00:00:00.000Z\"\n}"

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

**`Expiry Date`**

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

HttpResponse<String> response = Unirest.post("https://cdn.emnify.net/api/v1/application_token")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"description\": \"Token with expiry date\",\n  \"expiry_date\": \"2021-05-29T00:00:00.000Z\"\n}")
  .asString();
```

**`Expiry Date`**

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://cdn.emnify.net/api/v1/application_token', [
  'body' => '{
  "description": "Token with expiry date",
  "expiry_date": "2021-05-29T00:00:00.000Z"
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

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

**`Expiry Date`**

```csharp Expiry Date
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/application_token");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"description\": \"Token with expiry date\",\n  \"expiry_date\": \"2021-05-29T00:00:00.000Z\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Expiry Date`**

```swift Expiry Date
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = [
  "description": "Token with expiry date",
  "expiry_date": "2021-05-29T00:00:00.000Z"
] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/application_token")! 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()
```

### Response (200)

```json
{
  "application_token": "KAOp24TuMgjO2FpZmZ3ZFjSqpk7ea_mY8H2daMlMXF-lRbmMzLeQwSEX67-NFczI3GgHcHpCKTfAw"
}
```

You can revoke the application token at any time.

> **Tip**
>
> You can also create and view application tokens in the emnify Portal.
>
> For step-by-step instructions, see [Application tokens](/portal/application-tokens).

## Authenticate with an application token

Once you create an application token, use `/api/v1/authenticate` to generate a JWT `auth_token` that authenticates subsequent API calls.

### Request

POST [https://cdn.emnify.net/api/v1/authenticate](https://cdn.emnify.net/api/v1/authenticate)

**`Application Token Authentication`**

```curl Application Token Authentication
curl -X POST https://cdn.emnify.net/api/v1/authenticate \
     -H "Content-Type: application/json" \
     -d '{
  "application_token": "5cCI6IkpXVCJ9.."
}'
```

**`Application Token Authentication`**

```python Application Token Authentication
import requests

url = "https://cdn.emnify.net/api/v1/authenticate"

payload = { "application_token": "5cCI6IkpXVCJ9.." }
headers = {"Content-Type": "application/json"}

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

print(response.json())
```

**`Application Token Authentication`**

```javascript Application Token Authentication
const url = 'https://cdn.emnify.net/api/v1/authenticate';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"application_token":"5cCI6IkpXVCJ9.."}'
};

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

**`Application Token Authentication`**

```go Application Token Authentication
package main

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

func main() {

	url := "https://cdn.emnify.net/api/v1/authenticate"

	payload := strings.NewReader("{\n  \"application_token\": \"5cCI6IkpXVCJ9..\"\n}")

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

	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))

}
```

**`Application Token Authentication`**

```ruby Application Token Authentication
require 'uri'
require 'net/http'

url = URI("https://cdn.emnify.net/api/v1/authenticate")

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

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"application_token\": \"5cCI6IkpXVCJ9..\"\n}"

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

**`Application Token Authentication`**

```java Application Token Authentication
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://cdn.emnify.net/api/v1/authenticate")
  .header("Content-Type", "application/json")
  .body("{\n  \"application_token\": \"5cCI6IkpXVCJ9..\"\n}")
  .asString();
```

**`Application Token Authentication`**

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://cdn.emnify.net/api/v1/authenticate', [
  'body' => '{
  "application_token": "5cCI6IkpXVCJ9.."
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

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

**`Application Token Authentication`**

```csharp Application Token Authentication
using RestSharp;

var client = new RestClient("https://cdn.emnify.net/api/v1/authenticate");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"application_token\": \"5cCI6IkpXVCJ9..\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

**`Application Token Authentication`**

```swift Application Token Authentication
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = ["application_token": "5cCI6IkpXVCJ9.."] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "https://cdn.emnify.net/api/v1/authenticate")! 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()
```

### Response (200)

```json
{
  "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

> **Info**
>
> The server returns only an `auth_token`.
> The response doesn't include a `refresh_token`.

## Handle token expiration

When an application token expires or is revoked:

* Any `auth_token` generated from it remains valid until its own expiration
* New authentication attempts with the expired application token fail with a `401 Unauthorized` error
* You need to create a new application token and update your application configuration

To avoid service interruptions:

1. Set up monitoring for authentication failures
2. Create a new application token before the current one expires
3. Update your application to use the new token
4. Revoke the old token after the transition is complete

## Use an SDK

If you prefer to use an SDK instead of direct API calls:

* [Python SDK Quickstart](/developers/sdks/python/quickstart)
* [Java SDK Quickstart](/developers/sdks/java/quickstart)