TEMPORABLE API
Temporable was born because we needed a disposable email service with a great, easy to use API. At that time we were working on a project on which we needed service like that and none of the available options did fit us so we ended up creating our own solution, and years later, offered it to the public.
At this point we offer two API integrations, one for the Mail API and another for the Proxy API, you can use the API you have purchased the subscription for.
When we designed the API one of the main goals we had was that it should be easy and painless to integrate and use, the idea was that anybody with basic programming skills could use the API to reach their goals, we do not have overly complicated authentication methods, just the old and good API keys.
All the interaction between the API is done via HTTP requests to our API endpoint and we use the HTTP verbs POST GET DELETE and JSON to perform actions on the different services we offer.
OBTAINING THE API TOKEN
In order to start calling our API you need a API token/key, which is provided when you buy any of our plans.
The amount of API tokens you can create is limited by your purchased subscription, refer to the Security section in the console to see your current token limit.
All the API tokens share a set of limits which are based on the subscription plan you purchased.
Proxy API tokens does not have any request limits.
The same API token can be used for both APIs but each one have their own limits.
In order to create an API token you must visit our console and refer to the Security menu to manage your keys.
API SCHEMA DEFINITION
When calling our API you must follow a set of rules to avoid issues, specially when it comes down to errors and handling of the returned data.
Each section of this document explains the details of each individual service along with the returned data format, however, all of them shares a common structure
{
messageId: 321
}
{
"error": null,
"success": true,
"data": {
"id": 32,
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Verify your account at SomePhishingWebsite!",
"senderIp": "10.0.23.2",
"senderHostname": "local.temporal.email.com",
"headers": [],
"cc": [],
"bcc": [],
"messageId": "",
"inReplyTo": "",
"content" : {
"html": "",
"text": ""
},
"attachments": []
}
}
There you can see a example call to the GET /v1/messages endpoint, the request body only includes the needed JSON-encoded parameters to perform the call and the response follows this pattern, see below.
{
"error": "<error list>",
"success": "<true if the call suceeded or false otherwise>",
"data": "<response data for the call>"
}
RESPONSE OBJECT
Parameter | Type | Description |
---|
error | null | TemporableAPIError | The error parameter contains information about the reason a call has failed. If the call suceeded the value will be null or if it fails it will contain a TemporableAPIError object containing the reason. |
success | boolean | If the request succeeded it will be true and false otherwise, to check the reason of the failure check the error field. |
data | any | array<any> | The format of the data field is dependent upon the service being called, usually contains the response data or null if there's none. |
ERROR OBJECT
Parameter | Type | Description |
---|
code | string | Short error code describing the reason of the request failure, check the error handling section to see the list of errors. |
message | string | Human readable message describing the reason the request has failed. |
CALLING THE API
All the API transmission is done over HTTP using standard HTTP messages and verbs to the following API endpoint.
https:
The usage of HTTPS is mandatory, do not attempt to use HTTP as we don't even run a API service on HTTP.
Messages are authenticated using your API key and the HTTP header Authentication
. When using any HTTP client library use your API key as your username and leave the password empty.
In this example 9b54f54a09e23ce0da0107848b1707769053fefc1f005c782b6200a431439e17
is our API key.
GET /v1/message HTTP/1.1
...
Host: api.temporable.com
Authentication: Bearer 9b54f54a09e23ce0da0107848b1707769053fefc1f005c782b6200a431439e17
Content-Type: application/json
...
{
messageId: 23
}
You can authenticate the API using the apiToken
URL parameter but the recommended way is to use the Authentication
HTTP header. If you still want to use the apiToken
URL parameter, call the API as follows.
https:
We recommend you to use a HTTP library of your choice to communicate with the API.
ERRORS & HANDLING
Things go wrong sometimes and having a good error handling mechanism is mandatory, when developing your app ALWAYS add error handling code, even if something never fails, it will fail eventually, thank you Murphy.
The Temporable API manages errors via the error
field in the API responses, that field is designed to be very versatile and to allow us to implement specific error codes for specific scenarios, we could simply leave the success
field alone and call it done however we believe that being verbose with what failed in our side will help the developer find the problem and fix it quickly.
Each API service does have custom error codes and messages however we have defined a common set of errors that are shared between all the services of the API, it's a common practise to create a common error handler in your app to handle all the common errors and then catch the more specific errors on each call.
For example, imagine that we're doing some manteinance on the Temporable API servers and the API returns with the error service_unavailable
to all the calls, you could then make a generic error handler that queues the calls for later, when the API might be operative again instead of hammering our servers with calls that leads nowhere.
HTTP ERRORS
All the API calls, either succeeded or failed, returns the HTTP 200/201 status codes, if any other status code is returned (500 for example) treat it as a API downtime scenario.
COMMON ERRORS
error.code field | Description | Recommended Action |
---|
service_unavailable | The service is currently not available. | Wait some time before making another call |
service_overquota | The Account attached to the key ran out of alloted resources. | Renew your subscription to get more resources. |
service_ratelimit | The API key is being rate limited. | Try to slow down the request rate to avoid hitting the request limit. The limits are subscription-based |
service_internalerror | An internal, unexpected error has ocurred. | A temporal failure has ocurred in the system, retry the request again. |
key_invalid | The API key is invalid or does not exists | Verify that you're using a valid API key and that you copied all the characters. |
key_expired | The API key has expired. | The Account attached to the key doesn't have any active subscription |
entity_invalid | The specified entity does not exists or is invalid | |
entity_expired | The specified entity has expired and it's no longer accesible | |
parameter_invalid | Any of the input parameters are invalid. | |
parameter_missing | A required parameter is missing from the request. | |
access_denied | You don't have access to this feature | |
GET THE LIST OF PROXIES
REQUEST
GET /v1/proxy/list?<filters>
Below is the list of filters for the Proxy service, filters are appended to the URL in the form of URL parameters
You can specify multiple filtering values by repeating the filter in the URL
See the examples below.
GET /v1/proxy/list?country=AR
GET /v1/proxy/list?country=AR&country=CL
GET /v1/proxy/list?country=US&protocol=socks4
GET /v1/proxy/list?country=US&protocol=http&protocol=socks5
Filter Name | Type | Description |
---|
ip | IPv4 | IP address to match. |
port | integer | Port number to match. |
country | string | Country or list of countries to match, in ISO 3166-1 alfa-2 format |
protocol | string | Protocol of the proxy, can be any of http, socks4 or socks5 |
connectionTime | integer | Elapsed time until the TCP connection is established. |
firstTimeByte | integer | Elapsed time until the response is received. |
downloadSpeed | integer | Minimum proxy download speed, in bytes/second |
uptime | integer | Minimum uptime, in percentage 0-100 |
returnAll | boolean | Set to 1 or true to return all the matching proxies, else it will return a random one. |
anonymity | string | Filter out proxies based on the anonymity level provided, as follows.transparent - Transparent proxies pass your original IP to the target, they can see who you are.anonymous - Anonymous proxies identifies themselves as proxies to targets but does not pass your origin IP to the target.elite - Elite or High Anonymity proxies neither identifies themselves as proxies or pass any of your info to the target.
|
supports | string | Filter out proxies by features supported, see the list below, in order to check for several features separate them with commas.https - Supports FULL HTTPS connection end-to-end via HTTP tunneling.insecurehttps - Supports HTTPS but via a GET request, this is called insecure HTTPS because the proxy can actually see the data you send or receive.cookies - Proxy doesn't tamper or remove the Cookie header.useragent - Proxy doesn't tamper or remove the User-Agent header.referer - Proxy doesn't tamper or remove the Referer header.tunneling - Proxy supports the HTTP CONNECT method to create HTTP tunnels to port 80 tcptunneling - Proxy supports the HTTP CONNECT method to create HTTP tunnels to any TCP port.ws - Proxy supports WebSocket connections.wss - Proxy supports TLS/HTTPS WebSocket connections.
|
RESPONSE
{
"data":{
"level":"ELITE",
"host":"222.245.132.24",
"port":7302,
"type":"socks5",
"supports":{
"https":true,
"cookies":true,
"referer":true,
"tunneling":false,
"userAgent":true,
"webSocket":true,
"tunnelingTCP":false,
"insecureHttps":false,
"secureWebSocket":true
},
"stats":{
"downloadSpeed":0,
"firstByteTime":0,
"connectionTime":380
},
"country":"CN"
},
"error":null,
"success":true
}
Parameter Name | Type | Description |
---|
[].id | integer | Unique message identifier |
[].read | boolean | Whether the message has been read or not. Retrieving the message using the GET /v1/message service will mark the message as read. |
[].from | string | Source message address. |
[].to | array<string> | List of destination addresses |
[].subject | string | Message subject, parsed from the headers. |
[].receivedAt | integer | Unix timestamp at which the message was received. |
[].expiresAt | integer | Unix timestamp at which the message will expire and be deleted. |
[].hasAttachments | boolean | Whether the message contains any attachments. |
ERRORS
Error code | Description |
---|
offset_invalid | The specified offset is invalid. |
LISTING RECEIVED MESSAGES
REQUEST
GET /v1/message/list/:offset
Parameter Name | Type | Description |
---|
:offset | integer | Offset to retrieve the messages from, the API returns max 100 messages, to get the message 101 for example you need to pass 100 as the offset. The API won't return any messages if the offset is larger than the count of available messages |
RESPONSE
{
"error": null,
"data": [{
"id": 1337,
"read": true,
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Verify your account at SomePhishingWebsite!",
"receivedAt": 1644153984,
"expiresAt": 1645154007
"hasAttachments": false
}, {
"id": 1352,
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Hey there, want some scam stuff?",
"receivedAt": 1642113421,
"expiresAt": 1643125216
"hasAttachments": true
}]
}
Parameter Name | Type | Description |
---|
[].id | integer | Unique message identifier |
[].read | boolean | Whether the message has been read or not. Retrieving the message using the GET /v1/message service will mark the message as read. |
[].from | string | Source message address. |
[].to | array<string> | List of destination addresses |
[].subject | string | Message subject, parsed from the headers. |
[].receivedAt | integer | Unix timestamp at which the message was received. |
[].expiresAt | integer | Unix timestamp at which the message will expire and be deleted. |
[].hasAttachments | boolean | Whether the message contains any attachments. |
ERRORS
Error code | Description |
---|
offset_invalid | The specified offset is invalid. |
LISTING CUSTOM DOMAIN RECEIVED MESSAGES
REQUEST
POST /v1/message/domain
{
domain: "yourcustomdomain.com",
offset: 0
}
Parameter Name | Type | Description |
---|
domain | string | Name of the domain to retrieve messages for. |
offset | integer | Offset to retrieve the messages from, the API returns max 100 messages, to get the message 101 for example you need to pass 100 as the offset. The API won't return any messages if the offset is larger than the count of available messages |
RESPONSE
{
"error": null,
"data": [{
id: 1,
read: false,
from: '[email protected]',
to: [ '[email protected]' ],
subject: 'Temporal email',
receivedAt: '2022-02-19T11:35:07.360Z'
}]
}
Parameter Name | Type | Description |
---|
[].id | integer | Unique message identifier |
[].read | boolean | Whether the message has been read or not. Retrieving the message using the GET /v1/message service will mark the message as read. |
[].from | string | Source message address. |
[].to | array<string> | List of destination addresses |
[].subject | string | Message subject, parsed from the headers. |
[].receivedAt | integer | Unix timestamp at which the message was received. |
ERRORS
Error code | Description |
---|
offset_invalid | The specified offset is invalid. |
entity_invalid | The specifie domain does not exists. |
POLLING FOR UNREAD MESSAGES
REQUEST
GET /v1/message/poll
RESPONSE
{
"error": null,
"data": [{
id: 1,
from: '[email protected]',
to: [ '[email protected]' ],
receivedAt: '2022-02-19T11:35:07.360Z'
}]
}
Parameter Name | Type | Description |
---|
[].id | integer | Unique message identifier |
[].from | string | Source message address. |
[].to | array<string> | List of destination addresses |
[].receivedAt | integer | Unix timestamp at which the message was received. |
RETRIEVING MESSAGES
REQUEST
GET /v1/message/:id
Parameter Name | Type | Description |
---|
:id | integer | ID of the message to retrieve. |
RESPONSE
{
"error": null,
"data": {
"id": 32,
"read": false,
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Verify your account at SomePhishingWebsite!",
"senderIp": "10.0.23.2",
"senderHostname": "local.temporal.email.com",
"receivedAt": 1644153984,
"expiresAt": 1645154007,
"matchedDomains": ["quickbrownfox.com"]
"headers": [],
"cc": [],
"bcc": [],
"messageId": "",
"inReplyTo": "",
"content" : {
"html": "",
"text": ""
},
"attachments": []
}
}
Parameter Name | Type | Description |
---|
id | integer | Unique message identifier |
read | boolean | Whether the message has been read or not. |
from | string | Source message address. |
to | array<string> | List of destination addresses |
subject | string | Message subject, parsed from the headers. |
senderIp | string | Sender network address, either IPv4 or IPv6 |
senderHostname | string | Reversed resolved hostname of senderIp |
receivedAt | integer | Unix timestamp at which the message was received. |
expiresAt | integer | Unix timestamp at which the message will expire and be deleted. |
matchedDomains | array<string> | List of domains that matched the to field, including your own custom domains. |
headers | array<object> | Array of objects for the list of headers. |
headers[].name | string | Lowercase name of the SMTP header |
headers[].value | string | Value of the SMTP header |
cc | array<string> | Array of carbon copy addresses. |
bcc | array<string> | Array of blind carbon copy addresses. |
messageId | string | Identifier of the message per the Message-ID field, do not use this as a unique identifier as it can be easily spoofed. |
inReplyTo | string | Value of the In-Reply-To header field. |
content | object | Message content body |
content.html | string | HTML body of the message, if present |
content.text | string | Plain text body of the message, if present |
attachments | array<object> | Array of message attachments |
attachments[].id | integer | API ID for this attachment, use this ID when working with the Attachments service |
attachments[].cid | integer | Content ID for this attachment |
attachments[].size | integer | Size in bytes for the attachment |
attachments[].fileName | string | File name (if any specified) for this attachment |
ERRORS
Error code | Description |
---|
entity_invalid | The message ID doesn't exists |
DELETING MESSAGES
REQUEST
DELETE /v1/message/:id
Parameter Name | Type | Description |
---|
:id | integer | ID of the message to delete. |
RESPONSE
{
"error": null,
"data": {
"deletedId": 32
}
}
Parameter Name | Type | Description |
---|
deletedId | integer | ID of the message deleted. |
ERRORS
Error code | Description |
---|
entity_invalid | The message ID doesn't exists or has been already deleted. |
LISTING ATTACHMENTS
REQUEST
GET /v1/attachment/list/:messageId
Parameter Name | Type | Description |
---|
:messageId | integer | ID of the message to list the attachments from. |
RESPONSE
{
"error": null,
"data": [{
"id": 2172,
"cid": "logo",
"size": 32144,
"fileName": "logo.png",
"contentType": "image/png",
"disposition": "inline",
"checksum": "a306e9271dcc3326d05a87aebf2b5f28737a71d2566ba8faa3a5bf68a2940031",
"contentUrl": "https://attachment.temporable.com/fIaHBYXafNWCyZPVRdVxZLoUGSyWhjmMLDgLlbiMexGqUMFioZhOdPDYrAQYTJSyvkdSmDMHxNTAOLJEAbzQezuLWgWqNdLweHPI"
"headers": [
{
"name": "content-type",
"value": "image/png"
},
{
"name": "content-disposition",
"value": "inline"
},
{
"name": "content-transfer-encoding",
"value": "base64"
},
{
"name": "content-id",
"value": "<logo>"
}
]
}, {
"id": 2173,
"cid": "antispamemail",
"size": 32144,
"fileName": "email_antispam.png",
"contentType": "image/png",
"disposition": "inline",
"checksum": "1d77bb025d23fdcc65a80b498ce7d959d7f7ddcd63c1d2b76bf72dca656816bb",
"headers": [
{
"name": "content-type",
"value": "image/png"
},
{
"name": "content-disposition",
"value": "inline"
},
{
"name": "content-transfer-encoding",
"value": "base64"
},
{
"name": "content-id",
"value": "<antispamemail>"
}
]
}]
}
Please refer to the GET /v1/attachment
endpoint for the parameter descriptions.
ERRORS
Error code | Description |
---|
entity_invalid | The message ID doesn't exists |
RETRIEVING MESSAGE ATTACHMENT
REQUEST
GET /v1/attachment/:id
Parameter Name | Type | Description |
---|
:id | integer | ID of the attachment to get |
RESPONSE
{
"error": null,
"data": {
"id": 2172,
"messageId": 3121,
"cid": "logo",
"size": 32144,
"fileName": "logo.png",
"contentType": "image/png",
"disposition": "inline",
"checksum": "a306e9271dcc3326d05a87aebf2b5f28737a71d2566ba8faa3a5bf68a2940031",
"contentUrl": "https://attachment.temporable.com/fIaHBYXafNWCyZPVRdVxZLoUGSyWhjmMLDgLlbiMexGqUMFioZhOdPDYrAQYTJSyvkdSmDMHxNTAOLJEAbzQezuLWgWqNdLweHPI"
"headers": [
{
"name": "content-type",
"value": "image/png"
},
{
"name": "content-disposition",
"value": "inline"
},
{
"name": "content-transfer-encoding",
"value": "base64"
},
{
"name": "content-id",
"value": "<logo>"
}
"createdAt": 1501740665
]
}
}
Parameter Name | Type | Description |
---|
id | integer | API ID of the attachment |
messageId | integer | ID of the parent message. |
cid | string | Content ID of the attachment |
size | integer | Size in bytes of the attachment |
fileName | string | File name of the attachment, if any is specified. |
contentType | string | MIME type of the attachment body |
disposition | string | Attachment disposition in the message. |
checksum | string | SHA256 hash of the attachment contents. |
contentUrl | string | URL of the attachment content, download it via a GET request using a HTTP client. |
headers | array<object> | Array of attachment headers |
headers[].name | string | Lowercase name of the header |
headers[].value | string | Raw value of the header |
createdAt | integer | UNIX timestamp when the attachment was created. |
ERRORS
Error code | Description |
---|
entity_invalid | The attachyment ID doesn't exists or belongs to a message that you don't own. |
GENERATING EMAIL ADDRESS
Just a quick note, you dont need to call this API service to generate a valid address, as long as the domain is valid you can send email to said domain and it will be delivered at Temporable
This service is just offered as a quick way to generate email addresses based on some parameters, use it when you need it.
REQUEST
POST /v1/domain/address
{
domain: null,
tag: null,
user: null,
expires: 7200
}
Parameter Name | Type | Description |
---|
domain | stringoptional | Name of the domain to use when generating the address, leave empty to use a random domain from the available list. |
tag | stringoptional | Name of the tag to filter which domains to use, you can use the special tags public and private to include the public or the private domain list. Only used if domain is null |
user | stringoptional | Address name value, specifing a name would lead to an address in the format [email protected] leaving it null will generate a random name |
expires | integeroptional | Duration of the email binding, defaults to 2 hours if not specified, see the /domain/bind service for more information. |
RESPONSE
Leaving everything default
{
"error": null,
"data": {
expires: "2022-02-15T20:21:49Z",
address: "[email protected]"
}
}
Parameter Name | Type | Description |
---|
expires | ISO 8601 Date | Expiration date in ISO 8601 format for the binding. |
address | string | Address generated and bound |
ERRORS
Error code | Description |
---|
domain_invalid | The domain isn't a valid domain name or does not exists. |
domain_tag_invalid | The domain tag isn't a valid tag name or does not exists |
domain_unavailable | No domains are available at this moment to generate an address. |
address_exists | There's already a existing binding for the specified address. |
BINDING AN EMAIL ADDRESS
REQUEST
POST /v1/domain/bind
{
address: "[email protected]",
expires: 7200
}
Parameter Name | Type | Description |
---|
address | string | Full domain address to bind, the domain must exist in our system in order to be used. |
expires | integeroptional | Number of seconds before the binding expires, leaving the field empty will create a binding that expires in two hours, you can specify any number of seconds up to 24 hours. If you need more time you can simply call this function again with the same address and it will overwrite the current binding with the new expiration. |
RESPONSE
{
"error": null,
"data": {
expires: "2022-02-15T20:21:49Z",
address: "[email protected]"
}
}
Parameter Name | Type | Description |
---|
expires | ISO 8601 Date | Expiration date in ISO 8601 format. |
address | string | Address bound |
overwritten | boolean | Wether the binding already existed and it was overwritten |
ERRORS
Error code | Description |
---|
domain_invalid | The domain isn't a valid domain name or does not exists. |
address_invalid | The address is invalid or is not formatted correctly. |
UNBINDING AN EMAIL ADDRESS
REQUEST
POST /v1/domain/unbind
{
address: null
}
Parameter Name | Type | Description |
---|
address | null | string | array<string> | Setting this parameter to null will remove all the bindings of the account, you can also specify a single bound address or an array of bound addresses. |
RESPONSE
{
"error": null,
"data": ["[email protected]", "[email protected]"]
}
LIST ALL AVAILABLE DOMAINS
REQUEST
GET /v1/domain
RESPONSE
{
"error": null,
"data": {
"public": ["fastmail.com", "temporalemail.com", "phishmenot.com"],
"private": ["helixmanufacturing.com", "quicklaborchange.com", "acmeindustries.com"],
"custom": ["temporable.com", "myownwebsite.com"]
}
}
Parameter Name | Type | Description |
---|
public[] | array<string> | List of public available domains for use, the list of domains can change quickly so be sure to not cache it for too long. |
private[] | array<string> | List of domains available for subscriptors, while those domains don't change as quickly as public ones, be sure to check from time to time if new domains are added or removed. |
custom[] | array<string> | Custom domains that you have added to our API, those domains can only be used by the API/account who created them. |
ADDING YOUR OWN DOMAINS
REQUEST
POST /v1/domain
{
"domain": {
"name": "yournewcustomdomain.com",
"tags": ["registration", "verification"]
}
}
Parameter Name | Type | Description |
---|
domain | string | array<string> | Either an array of domain objects or a single domain object. We recommend to configure the domain DNS MX records to point to Temporable before adding the domain to avoid messages failing to be delivered due to the MX records not being configured or not yet propagated across the internet. |
domain.name | string | Name of the domain to be added |
domain.tags | optional array<string> | List of tags to assign to the domain, the tags can be used at address generation time to specify a domain or groups of domains that shares a single tag. By assigning a tag to several domains you can generate a random address using said tag and randomly get any of the domains matching the tag. |
RESPONSE
{
"error": null,
"data": "yournewcustomdomain.com"
}
ERRORS
Error code | Description |
---|
domain_invalid | The domain isn't a valid domain name. |
domain_exists | The domain name already exists, either in your account or in another account. |
DELETING A DOMAIN
REQUEST
DELETE /v1/domain
{
"domain": "yournewcustomdomain.com"
}
Parameter Name | Type | Description |
---|
domain | string | Name of the domain to delete. At this point you can only delete your own custom domains, in future API version we will add the ability to disable public/private domains per request. |
RESPONSE
{
"error": null,
"data": null
}
ERRORS
Error code | Description |
---|
domain_invalid | The domain isn't a valid domain name or does not exists. |
WORKING WITH WEBHOOKS
WebHooks are the best way to get notified of incoming events in the API related with your domains
CREATING WEBHOOK FILTERS
RETRIEVING WEBHOOK DATA