# Loops Ruby SDK
## Introduction
This is the official Ruby SDK for [Loops](https://loops.so), an email platform for modern software companies.
## Installation
Install the gem and add it to the application's Gemfile like this:
```bash
bundle add loops_sdk
```
If bundler is not being used to manage dependencies, you can install the gem like this:
```bash
gem install loops_sdk
```
## Usage
You will need a Loops API key to use the package.
In your Loops account, go to the [API Settings page](https://app.loops.so/settings?page=api) and click **Generate key**.
Copy this key and save it in your application code (for example, in an environment variable).
See the API documentation to learn more about [rate limiting](https://loops.so/docs/api-reference#rate-limiting) and [error handling](https://loops.so/docs/api-reference#debugging).
In an initializer, import and configure the SDK:
```ruby config/initializers/loops.rb
require "loops_sdk"
LoopsSdk.configure do |config|
config.api_key = 'your_api_key'
end
```
```ruby
begin
response = LoopsSdk::Transactional.send(
transactional_id: "closfz8ui02yq......",
email: "dan@loops.so",
data_variables: {
loginUrl: "https://app.domain.com/login?code=1234567890"
}
)
render json: response
rescue LoopsSdk::APIError => e
# Do something if there is an error from the API
end
```
## Handling rate limits
You can use the check for rate limit issues with your requests.
You can access details about the rate limits from the `limit` and `remaining` attributes.
```ruby
begin
response = LoopsSdk::Contacts.update(
email: "dan@loops.so"
)
render json: response
rescue LoopsSdk::RateLimitError => e
Rails.logger.error("Rate limit exceeded (#{e.limit} requests per second)")
# Code here to re-try this request
rescue LoopsSdk::APIError => e
# Handle other errors
end
```
## Default contact properties
Each contact in Loops has a set of default properties. These will always be returned in API results.
- `id`
- `email`
- `firstName`
- `lastName`
- `source`
- `subscribed`
- `userGroup`
- `userId`
## Custom contact properties
You can use custom contact properties in API calls. Please make sure to [add custom properties](https://loops.so/docs/contacts/properties#custom-contact-properties) in your Loops account before using them with the SDK.
## Methods
- [ApiKey.test()](#apikey-test)
- [Contacts.create()](#contacts-create)
- [Contacts.update()](#contacts-update)
- [Contacts.find()](#contacts-find)
- [Contacts.delete()](#contacts-delete)
- [MailingLists.list()](#mailinglists-list)
- [Events.send()](#events-send)
- [Transactional.send()](#transactional-send)
- [CustomFields.list()](#customfields-list)
---
### ApiKey.test()
Test if your API key is valid.
[API Reference](https://loops.so/docs/api-reference/api-key)
#### Parameters
None
#### Example
```ruby
response LoopsSdk::ApiKey.test
```
#### Response
This method will return a success or error message:
```json
{
"success": true,
"teamName": "Company name"
}
```
```json
{
"error": "Invalid API key"
}
```
---
## Contacts.create()
Create a new contact.
[API Reference](https://loops.so/docs/api-reference/create-contact)
#### Parameters
| Name | Type | Required | Notes |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email` | string | Yes | If a contact already exists with this email address, an error response will be returned. |
| `properties` | object | No | An object containing default and any custom properties for your contact.
Please [add custom properties](https://loops.so/docs/contacts/properties#custom-contact-properties) in your Loops account before using them with the SDK.
Values can be of type `string`, `number`, `nil` (to reset a value), `boolean` or `date` ([see allowed date formats](https://loops.so/docs/contacts/properties#dates)). |
| `mailing_lists` | object | No | An object of mailing list IDs and boolean subscription statuses. |
#### Examples
```ruby
response = LoopsSdk::Contacts.create(email: "hello@gmail.com")
contact_properties = {
firstName: "Bob" /* Default property */,
favoriteColor: "Red" /* Custom property */,
};
mailing_lists = {
cm06f5v0e45nf0ml5754o9cix: true,
cm16k73gq014h0mmj5b6jdi9r: false,
};
response = LoopsSdk::Contacts.create(
email: "hello@gmail.com",
properties: contact_properties,
mailing_lists: mailing_lists
)
```
#### Response
This method will return a success or error message:
```json
{
"success": true,
"id": "id_of_contact"
}
```
```json
{
"success": false,
"message": "An error message here."
}
```
---
## Contacts.update()
Update a contact.
Note: To update a contact's email address, the contact requires a `userId` value. Then you can make a request with their `userId` and an updated email address.
[API Reference](https://loops.so/docs/api-reference/update-contact)
#### Parameters
| Name | Type | Required | Notes |
| --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `email` | string | Yes | The email address of the contact to update. If there is no contact with this email address, a new contact will be created using the email and properties in this request. |
| `properties` | object | No | An object containing default and any custom properties for your contact.
Please [add custom properties](https://loops.so/docs/contacts/properties#custom-contact-properties) in your Loops account before using them with the SDK.
Values can be of type `string`, `number`, `nil` (to reset a value), `boolean` or `date` ([see allowed date formats](https://loops.so/docs/contacts/properties#dates)). |
| `mailing_lists` | object | No | An object of mailing list IDs and boolean subscription statuses. |
#### Example
```ruby
contact_properties = {
firstName: "Bob" /* Default property */,
favoriteColor: "Blue" /* Custom property */,
};
response = LoopsSdk::Contacts.update(
email: "hello@gmail.com",
properties: contact_properties
)
# Updating a contact's email address using userId
response = LoopsSdk::Contacts.update(
email: "newemail@gmail.com",
properties: {
userId: "1234",
})
```
#### Response
This method will return a success or error message:
```json
{
"success": true,
"id": "id_of_contact"
}
```
```json
{
"success": false,
"message": "An error message here."
}
```
---
### Contacts.find()
Find a contact.
[API Reference](https://loops.so/docs/api-reference/find-contact)
#### Parameters
You must use one parameter in the request.
| Name | Type | Required | Notes |
| --------- | ------ | -------- | ----- |
| `email` | string | No | |
| `user_id` | string | No | |
#### Examples
```ruby
response = LoopsSdk::Contacts.find(email: "hello@gmail.com")
response = LoopsSdk::Contacts.find(user_id: "12345")
```
#### Response
This method will return a list containing a single contact object, which will include all default properties and any custom properties.
If no contact is found, an empty list will be returned.
```json
[
{
"id": "cll6b3i8901a9jx0oyktl2m4u",
"email": "hello@gmail.com",
"firstName": "Bob",
"lastName": null,
"source": "API",
"subscribed": true,
"userGroup": "",
"userId": "12345",
"mailingLists": {
"cm06f5v0e45nf0ml5754o9cix": true
},
"favoriteColor": "Blue" /* Custom property */
}
]
```
---
### Contacts.delete()
Delete a contact.
[API Reference](https://loops.so/docs/api-reference/delete-contact)
#### Parameters
You must use one parameter in the request.
| Name | Type | Required | Notes |
| --------- | ------ | -------- | ----- |
| `email` | string | No | |
| `user_id` | string | No | |
#### Example
```ruby
response = LoopsSdk::Contacts.delete(email: "hello@gmail.com")
response = LoopsSdk::Contacts.delete(user_id: "12345")
```
#### Response
This method will return a success or error message:
```json
{
"success": true,
"message": "Contact deleted."
}
```
```json
{
"success": false,
"message": "An error message here."
}
```
---
### MailingLists.list()
Get a list of your account's mailing lists. [Read more about mailing lists](https://loops.so/docs/contacts/mailing-lists)
[API Reference](https://loops.so/docs/api-reference/list-mailing-lists)
#### Parameters
None
#### Example
```ruby
response = LoopsSdk::MailingLists.list
```
#### Response
This method will return a list of mailing list objects containing `id`, `name` and `isPublic` attributes.
If your account has no mailing lists, an empty list will be returned.
```json
[
{
"id": "cm06f5v0e45nf0ml5754o9cix",
"name": "Main list",
"isPublic": true
},
{
"id": "cm16k73gq014h0mmj5b6jdi9r",
"name": "Investors",
"isPublic": false
}
]
```
---
### Events.send()
Send an event to trigger an email in Loops. [Read more about events](https://loops.so/docs/events)
[API Reference](https://loops.so/docs/api-reference/send-event)
#### Parameters
| Name | Type | Required | Notes |
| -------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_name` | string | Yes | |
| `email` | string | No | The contact's email address. Required if `user_id` is not present. |
| `user_id` | string | No | The contact's unique user ID. If you use `user_id` without `email`, this value must have already been added to your contact in Loops. Required if `email` is not present. |
| `contact_properties` | object | No | An object containing contact properties, which will be updated or added to the contact when the event is received.
Please [add custom properties](https://loops.so/docs/contacts/properties#custom-contact-properties) in your Loops account before using them with the SDK.
Values can be of type `string`, `number`, `nil` (to reset a value), `boolean` or `date` ([see allowed date formats](https://loops.so/docs/contacts/properties#dates)). |
| `event_properties` | object | No | An object containing event properties, which will be made available in emails that are triggered by this event.
Values can be of type `string`, `number`, `boolean` or `date` ([see allowed date formats](https://loops.so/docs/events/properties#important-information-about-event-properties)). |
| `mailing_lists` | object | No | An object of mailing list IDs and boolean subscription statuses. |
#### Examples
```ruby
response = LoopsSdk::Events.send(
event_name: "signup",
email: "hello@gmail.com"
)
response = LoopsSdk::Events.send(
event_name: "signup",
email: "hello@gmail.com",
event_properties: {
username: "user1234",
signupDate: "2024-03-21T10:09:23Z",
},
mailing_lists: {
cm06f5v0e45nf0ml5754o9cix: true,
cm16k73gq014h0mmj5b6jdi9r: false,
},
})
# In this case with both email and userId present, the system will look for a contact with either a
# matching `email` or `user_id` value.
# If a contact is found for one of the values (e.g. `email`), the other value (e.g. `user_id`) will be updated.
# If a contact is not found, a new contact will be created using both `email` and `user_id` values.
# Any values added in `contact_properties` will also be updated on the contact.
response = LoopsSdk::Events.send(
event_name: "signup",
email: "hello@gmail.com",
user_id: "1234567890",
contact_properties: {
firstName: "Bob",
plan: "pro",
},
})
```
#### Response
This method will return a success or error:
```json
{
"success": true
}
```
```json
{
"success": false,
"message": "An error message here."
}
```
---
### Transactional.send()
Send a transactional email to a contact. [Learn about sending transactional email](https://loops.so/docs/transactional/guide)
[API Reference](https://loops.so/docs/api-reference/send-transactional-email)
#### Parameters
| Name | Type | Required | Notes |
| ---------------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `transactional_id` | string | Yes | The ID of the transactional email to send. |
| `email` | string | Yes | The email address of the recipient. |
| `add_to_audience` | boolean | No | If `true`, a contact will be created in your audience using the `email` value (if a matching contact doesn’t already exist). |
| `data_variables` | object | No | An object containing data as defined by the data variables added to the transactional email template.
Values can be of type `string` or `number`. |
| `attachments` | object[] | No | A list of attachments objects.
**Please note**: Attachments need to be enabled on your account before using them with the API. [Read more](https://loops.so/docs/transactional/attachments) |
| `attachments[].filename` | string | No | The name of the file, shown in email clients. |
| `attachments[].content_type` | string | No | The MIME type of the file. |
| `attachments[].data` | string | No | The base64-encoded content of the file. |
#### Examples
```ruby
response = LoopsSdk::Transactional.send(
transactional_id: "clfq6dinn000yl70fgwwyp82l",
email: "hello@gmail.com",
data_variables: {
loginUrl: "https://myapp.com/login/",
},
)
# Please contact us to enable attachments on your account.
response = LoopsSdk::Transactional.send(
transactional_id: "clfq6dinn000yl70fgwwyp82l",
email: "hello@gmail.com",
data_variables: {
loginUrl: "https://myapp.com/login/",
},
attachments: [
{
filename: "presentation.pdf",
content_type: "application/pdf",
data: "JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPD...",
},
],
})
```
#### Response
This method will return a success or error message.
```json
{
"success": true
}
```
If there is a problem with the request, a descriptive error message will be returned:
```json
{
"success": false,
"path": "dataVariables",
"message": "There are required fields for this email. You need to include a 'dataVariables' object with the required fields."
}
```
```json
{
"success": false,
"error": {
"path": "dataVariables",
"message": "Missing required fields: login_url"
},
"transactionalId": "clfq6dinn000yl70fgwwyp82l"
}
```
---
### CustomFields.list()
Get a list of your account's custom fields. These are custom properties that can be added to contacts to store extra data. [Read more about contact properties](https://loops.so/docs/contacts/properties)
[API Reference](https://loops.so/docs/api-reference/list-custom-fields)
#### Parameters
None
#### Example
```ruby
response LoopsSdk::CustomFields.list
```
#### Response
This method will return a list of custom field objects containing `key`, `label` and `type` attributes.
If your account has no custom fields, an empty list will be returned.
```json
[
{
"key": "favoriteColor",
"label": "Favorite Color",
"type": "string"
},
{
"key": "plan",
"label": "Plan",
"type": "string"
}
]
```
---
## Contributing
Bug reports and pull requests are welcome. Please read our [Contributing Guidelines](CONTRIBUTING.md).