Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions components/sms_florin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Overview

The [sms-florin](https://flo-voice1.com) API lets you rent real UK mobile numbers and receive SMS/OTP verification codes on them programmatically — handy for testing signup and verification flows without burning a personal number.

With these components you can:

- Rent a number for a specific service (WhatsApp, Telegram, Google, and more)
- Read a rental's status, phone number, and any codes received
- Trigger a workflow the moment a code lands on one of your numbers

# Example Use Cases

- **End-to-end signup test** – *Rent a Number* for the service under test, drive the signup in another step, then *Get Rental* to pull the verification code.
- **Route incoming codes** – use the *New SMS Received* trigger to forward every code to Slack, a database, or a test runner.
- **Scheduled account provisioning** – on a timer, rent a number, wait for the activation code, and hand both to a downstream system.

# Getting Started

1. Create an account at [flo-voice1.com](https://flo-voice1.com) and add balance.
2. Generate an API key on the [API access page](https://flo-voice1.com/api-access).
3. In Pipedream, add the sms-florin app and paste the key when prompted.

# Troubleshooting

- **`invalid or missing API key`** – the key is wrong or was revoked. Generate a new one on the API access page and update the connected account in Pipedream.
- **`Rent a Number` fails with an insufficient-balance error** – top up your account balance; each rental is charged up front.
- **`New SMS Received` emits nothing** – a code only appears after the target service actually sends one to the rented number, and the trigger only looks at your 25 most recent rentals.
31 changes: 31 additions & 0 deletions components/sms_florin/actions/get-rental/get-rental.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import app from "../../sms_florin.app.mjs";

export default {
key: "sms_florin-get-rental",
name: "Get Rental",
description: "Retrieve a rental's status, phone number, and any SMS received so far. [See the documentation](https://flo-voice1.com/api-access).",
version: "0.0.1",
type: "action",
annotations: {
destructiveHint: false,
openWorldHint: true,
readOnlyHint: true,
},
props: {
app,
rentalId: {
propDefinition: [
app,
"rentalId",
],
},
},
async run({ $ }) {
const rental = await this.app.getRental({
$,
rentalId: this.rentalId,
});
$.export("$summary", `Retrieved rental ${this.rentalId} (status: ${rental.status})`);
return rental;
},
};
40 changes: 40 additions & 0 deletions components/sms_florin/actions/rent-number/rent-number.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import app from "../../sms_florin.app.mjs";

export default {
key: "sms_florin-rent-number",
name: "Rent a Number",
description: "Rent a phone number for a service, debiting your account balance. [See the documentation](https://flo-voice1.com/api-access).",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
version: "0.0.1",
type: "action",
annotations: {
destructiveHint: false,
openWorldHint: true,
readOnlyHint: false,
},
props: {
app,
serviceSlug: {
propDefinition: [
app,
"serviceSlug",
],
},
period: {
propDefinition: [
app,
"period",
],
},
},
async run({ $ }) {
const response = await this.app.rentNumber({
$,
data: {
serviceSlug: this.serviceSlug,
period: this.period,
},
});
$.export("$summary", `Successfully rented a number (rental ID ${response.rentalId})`);
return response;
},
};
18 changes: 18 additions & 0 deletions components/sms_florin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@pipedream/sms_florin",
"version": "0.0.1",
"description": "Pipedream sms-florin Components",
"main": "sms_florin.app.mjs",
"keywords": [
"pipedream",
"sms_florin"
],
"homepage": "https://pipedream.com/apps/sms_florin",
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@pipedream/platform": "^3.1.0"
}
}
86 changes: 86 additions & 0 deletions components/sms_florin/sms_florin.app.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { axios } from "@pipedream/platform";

export default {
type: "app",
app: "sms_florin",
propDefinitions: {
serviceSlug: {
type: "string",
label: "Service",
description: "The service to rent a number for (e.g. `whatsapp`, `telegram`, `google`).",
async options() {
const { services } = await this.listServices();
return services?.map(({
slug, name,
}) => ({
label: name,
value: slug,
})) || [];
},
},
period: {
type: "string",
label: "Period",
description: "How long to hold the number. `instant` is a short rental for a single code; `monthly` keeps the number for 30 days.",
options: [
"instant",
"monthly",
],
default: "instant",
},
rentalId: {
type: "integer",
label: "Rental ID",
description: "The ID of a rental, as returned by **Rent a Number**.",
},
},
methods: {
_baseUrl() {
return "https://flo-voice1.com/api/v1";
},
async _makeRequest({
$, path, ...opts
}) {
return axios($ || this, {
url: `${this._baseUrl()}${path}`,
headers: {
Authorization: `Bearer ${this.$auth.api_key}`,
},
...opts,
});
},
async listServices(opts = {}) {
return this._makeRequest({
path: "/services",
...opts,
});
},
async rentNumber({
data, ...opts
}) {
return this._makeRequest({
method: "POST",
path: "/rentals",
data,
...opts,
});
},
async listRentals({
params, ...opts
} = {}) {
return this._makeRequest({
path: "/rentals",
params,
...opts,
});
},
async getRental({
rentalId, ...opts
}) {
return this._makeRequest({
path: `/rentals/${rentalId}`,
...opts,
});
},
},
};
84 changes: 84 additions & 0 deletions components/sms_florin/sources/new-sms/new-sms.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
import app from "../../sms_florin.app.mjs";
import sampleEmit from "./test-event.mjs";

export default {
key: "sms_florin-new-sms",
name: "New SMS Received",
description: "Emit a new event each time an SMS arrives on one of your rented numbers.",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
version: "0.0.1",
type: "source",
dedupe: "unique",
props: {
app,
db: "$.service.db",
timer: {
type: "$.interface.timer",
default: {
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
},
},
},
methods: {
_getLastId() {
return this.db.get("lastId") || 0;
},
_setLastId(lastId) {
this.db.set("lastId", lastId);
},
async getNewMessages(lastId) {
const { rentals } = await this.app.listRentals({
params: {
limit: 25,
},
});
const events = [];
for (const rental of rentals || []) {
for (const message of rental.messages || []) {
if (message.id > lastId) {
events.push({
rental,
message,
});
}
}
}
// Oldest first, so events are emitted in chronological order and the
// greatest id is the last one persisted.
return events.sort((a, b) => a.message.id - b.message.id);
},
emitEvent({
rental, message,
}) {
this.$emit({
...message,
rentalId: rental.id,
service: rental.service?.name || null,
serviceSlug: rental.service?.slug || null,
phoneNumber: rental.phoneNumber,
country: rental.country,
}, {
id: message.id,
summary: `New SMS on ${rental.phoneNumber || rental.service?.name || "rented number"}`,
ts: Date.parse(message.receivedAt) || Date.now(),
});
},
},
hooks: {
async deploy() {
// Don't replay codes that arrived before the source was set up — just
// record the current high-water mark.
const events = await this.getNewMessages(0);
const lastId = events.at(-1)?.message?.id || 0;
this._setLastId(lastId);
},
},
async run() {
const events = await this.getNewMessages(this._getLastId());
for (const event of events) {
this.emitEvent(event);
this._setLastId(event.message.id);
}
},
sampleEmit,
};
11 changes: 11 additions & 0 deletions components/sms_florin/sources/new-sms/test-event.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
"id": 1024,
"sender": "WhatsApp",
"body": "Your WhatsApp code is 123-456",
"receivedAt": "2026-08-07T12:00:00.000Z",
"rentalId": 42,
"service": "WhatsApp",
"serviceSlug": "whatsapp",
"phoneNumber": "+447700900123",
"country": "GB"
}