From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use Moops -strict;
# ABSTRACT: interfaces with Intercom.io
=pod
=head1 NAME
WebService::Intercom - interact with the Intercom.io API
=head1 SYNOPSIS
my $intercom = WebService::Intercom->new(app_id => '[APP ID]',
api_key => '[API KEY]');
# Create a user
my $user = $intercom->user_create_or_update(
email => 'test@example.com',
name => 'test user');
# Retrieve an existing user.
my $existing_user = $intercom->user_get(email => 'test2@example.com');
# Add a tag to a user
$user->tag('test tag');
$intercom->tag_create_or_update(name => "test tag");
$intercom->tag_items(name => "test tag", users => [{ email => 'test@example.com'}]);
$user->untag('test tag');
# Change the user's name
$user->name = 'new name';
$user->save();
# Delete the user
$user->delete();
$intercom->user_delete(email => 'test@example.com');
# Add a note
$user->add_note(body => "This is a test note");
$intercom->note_create(email => 'test@example.com',
body => "This is a test note");
# Add an event
$user->add_event(event_name => 'test event');
$intercom->event_create(email => 'test@example.com',
event_name => 'test event',
metadata => {
"article" => {"url" => "https://example.org/",
"value" => "link text"},
});
=head1 DESCRIPTION
Provides a nice API for Intercom.io rather than making raw requests.
=head1 IMPLEMENTATION PHILOSOPHY
This module attempts to stick as close to the API as possible.
Documentation for the v2 API:
For examples see the test cases, most functionality is well exercised
via tests.
=cut
class WebService::Intercom types WebService::Intercom::Types {
use strict;
use warnings;
use HTTP::Request::Common qw(DELETE POST GET);
use JSON::XS;
use Kavorka qw( multi method );
has 'ua' => (is => 'ro', default => sub { return LWP::UserAgent->new() } );
has 'app_id' => (is => 'ro');
has 'api_key' => (is => 'ro');
has 'api_base' => (is => 'ro', isa => Str, default => 'https://api.intercom.io');
=head1 FUNCTIONS
=head2 user_get
Retrieves an existing user.
$intercom->user_get(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
Maybe[Str] :$id?);
Only one of user_id, email or id are required to retrieve a user.
Returns a L<WebService::Intercom::User>.
=cut
method user_get(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
Maybe[Str] :$id?) {
if (!(defined($user_id) xor defined($email) xor defined($id))) {
WebService::Intercom::Exception->throw({ message => "One and only one of user_id, email or id must be defined"});
}
my $request;
if (defined($id)) {
$request = GET($self->api_base . '/users/' . $id);
} elsif (defined($user_id)) {
$request = GET($self->api_base . '/users?user_id=' . URI::Escape::uri_escape($user_id));
} elsif (defined($email)) {
$request = GET($self->api_base . '/users?email=' . URI::Escape::uri_escape($email));
}
$self->_request($request);
}
=head2 user_create_or_update
Creates or updates a user.
# When you have an existing WebService::Intercom::User
$intercom->user_create_or_update(WebService::Intercom::User $user);
or
$intercom->user_create_or_update(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
Maybe[Str] :$id?,
Maybe[Int] :$signed_up_at?,
Str :$name?,
Maybe[IPAddressType] :$last_seen_ip?,
CustomAttributesType :$custom_attributes?,
Maybe[Str] :$last_seen_user_agent?,
HashRef :$companies?,
Maybe[Int] :$last_request_at?,
Maybe[Bool] :$unsubscribed_from_emails?,
Maybe[Bool] :$update_last_request_at?,
Maybe[Bool] :$new_session?);
Returns a L<WebService::Intercom::User> that represents the new or updated user.
=cut
multi method user_create_or_update(WebService::Intercom::User $user) {
return $self->user_create_or_update(user_id => $user->user_id,
email => $user->email,
id => $user->id,
name => $user->name,
last_seen_ip => $user->last_seen_ip,
custom_attributes => $user->custom_attributes,
last_seen_user_agent => $user->user_agent_data,
last_request_at => $user->last_request_at,
unsubscribed_from_emails => $user->unsubscribed_from_emails,
);
}
multi method user_create_or_update(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
Maybe[Str] :$id?,
Maybe[Int] :$signed_up_at?,
Str :$name?,
Maybe[IPAddressType] :$last_seen_ip?,
CustomAttributesType :$custom_attributes?,
Maybe[Str] :$last_seen_user_agent?,
HashRef :$companies?,
Maybe[Int] :$last_request_at?,
Maybe[Bool] :$unsubscribed_from_emails?,
Maybe[Bool] :$update_last_request_at?,
Maybe[Bool] :$new_session?) {
my $json_content = {
signed_up_at => $signed_up_at,
name => $name,
last_seen_ip => $last_seen_ip,
custom_attributes => $custom_attributes,
last_seen_user_agent => $last_seen_user_agent,
companies => $companies,
last_request_at => $last_request_at,
unsubscribed_from_emails => $unsubscribed_from_emails,
update_last_request_at => $update_last_request_at,
new_session => $new_session
};
if (defined($user_id)) {
$json_content->{user_id} = $user_id;
}
if (defined($email)) {
$json_content->{email} = $email;
}
if (defined($id)) {
$json_content->{id} = $id;
}
my $json = JSON::XS::encode_json($json_content);
my $request = POST($self->api_base . '/users',
'Content-Type' => 'application/json',
Content => $json
);
$self->_request($request);
}
=head2 user_delete
Deletes a user
# When you have an existing WebService::Intercom::User
$intercom->user_delete(WebService::Intercom::User $user);
or
$intercom->user_delete(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
Maybe[Str] :$id?);
Only one of user_id, email or id is required.
Returns a L<WebService::Intercom::User> that represents the deleted user.
=cut
multi method user_delete(WebService::Intercom::User $user) {
return $self->user_delete(user_id => $user->user_id,
id => $user->id,
email => $user->email);
}
multi method user_delete(Str :$user_id?,
Str :$id?,
Str :$email?) {
my $request;
if (defined($id)) {
$request = DELETE($self->api_base . '/users/' . $id);
} elsif (defined($user_id)) {
$request = DELETE($self->api_base . '/users?user_id=' . URI::Escape::uri_escape($user_id));
} elsif (defined($email)) {
$request = DELETE($self->api_base . '/users?email=' . URI::Escape::uri_escape($email));
}
$self->_request($request);
}
=head2 tag_create_or_update
Creates or updates a tag.
# When you have an existing WebService::Intercom::User
$intercom->tag_create_or_update(WebService::Intercom::Tag $tag);
or
$intercom->tag_create_or_update(Str :$name,
Maybe[Str] :$id?);
Returns a L<WebService::Intercom::Tag> that represents the tag.
=cut
multi method tag_create_or_update(WebService::Intercom::Tag $tag) {
return $self->tag_create_or_update(name => $tag->name,
id => $tag->id);
}
multi method tag_create_or_update(Str :$name,
Maybe[Str] :$id?) {
my $json_content = {
name => $name,
};
if (defined($id)) {
$json_content->{id} = $id;
}
my $request = POST($self->api_base . '/tags',
'Content-Type' => 'application/json',
Content => JSON::XS::encode_json($json_content)
);
return $self->_request($request);
}
=head2 tag_items
Applies or removes a tag to users or companies
# When you have an existing WebService::Intercom::User
$intercom->tag_items(Str :$name,
ArrayRef[TagUserIdentifierType] :$users?,
ArrayRef[TagCompanyIdentifierType] :$companies?);
=cut
method tag_items(Str :$name,
ArrayRef[TagUserIdentifierType] :$users?,
ArrayRef[TagCompanyIdentifierType] :$companies?) {
my $json_content = {
name => $name,
};
if (!(defined($users) xor defined($companies))) {
WebService::Intercom::Exception->throw({ message => "Either users or companies must be defined as a parameter, not both and not neither"});
}
if (defined($users)) {
$json_content->{users} = $users;
} elsif (defined($companies)) {
$json_content->{companies} = $companies;
}
my $request = POST($self->api_base . '/tags',
'Content-Type' => 'application/json',
Content => JSON::XS::encode_json($json_content)
);
return $self->_request($request);
}
=head2 tag_delete
Deletes a tag
# When you have an existing WebService::Intercom::User
$intercom->tag_delete(WebService::Intercom::Tag $tag);
or
$intercom->tag_delete(Str :$id);
Returns undef
=cut
multi method tag_delete(Str :$id) {
$self->_request(DELETE($self->api_base . '/tags/' . $id), no_content => 1);
}
multi method tag_delete(WebService::Intercom::Tag $tag) {
return $self->tag_delete(id => $tag->id);
}
=head2 note_create
Creates a note for a user
# When you have an existing WebService::Intercom::User
$intercom->note_create(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
Maybe[Str] :$id?,
Maybe[Str] :$admin_id?,
Str :$body);
Returns a L<WebService::Intercom::Note> that represents the note.
=cut
method note_create(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
Maybe[Str] :$id?,
Maybe[Str] :$admin_id?,
Str :$body) {
if (!(defined($user_id) xor defined($email) xor defined($id))) {
WebService::Intercom::Exception->throw({ message => "One and only one of user_id, email or id must be defined"});
}
my $json_content = {
body => $body,
};
if (defined($user_id)) {
$json_content->{user}->{user_id} = $user_id;
} elsif (defined($email)) {
$json_content->{user}->{email} = $email;
} elsif (defined($id)) {
$json_content->{user}->{id} = $id;
}
my $request = POST($self->api_base . '/notes',
'Content-Type' => 'application/json',
Content => JSON::XS::encode_json($json_content)
);
return $self->_request($request);
}
=head2 event_create
Creates an event for a user
# When you have an existing WebService::Intercom::User
$intercom->event_create(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
EventNameType :$event_name,
Maybe[Int] :$created_at?,
Maybe[EventMetadataType] :$metadata?);
Returns undef.
=cut
method event_create(Maybe[Str] :$user_id?,
Maybe[Str] :$email?,
EventNameType :$event_name,
Maybe[Int] :$created_at?,
Maybe[EventMetadataType] :$metadata? where { !defined($_) || scalar(keys %$_) <= 5 }) {
if (!(defined($user_id) || defined($email))) {
WebService::Intercom::Exception->throw({ message => "One of user_id or email must be defined"});
}
my $json_content = {
event_name => $event_name,
created_at => $created_at,
};
if (defined($user_id)) {
$json_content->{user_id} = $user_id;
} elsif (defined($email)) {
$json_content->{email} = $email;
}
if (defined($metadata)) {
$json_content->{metadata} = $metadata;
}
my $request = POST($self->api_base . '/events',
'Content-Type' => 'application/json',
Content => JSON::XS::encode_json($json_content)
);
return $self->_request($request, no_content => 1);
}
method _request(HTTP::Request $request, Bool :$no_content?) {
$request->header('Accept', 'application/json');
$request->header('Authorization' => "Basic " . MIME::Base64::encode_base64($self->app_id . ":" . $self->api_key, ''));
my $response = $self->ua->request($request);
if ($response->is_success()) {
if (!$no_content) {
my $data;
eval {
$data = JSON::XS::decode_json($response->content());
};
if ($@) {
WebService::Intercom::Exception->throw({ message => "Failed to decode JSON result for request " . $request->as_string() . "\nResult was: " . $response->as_string()});
}
if ($data->{type} =~ /^(user|tag|note)$/) {
return ("WebService::Intercom::" . ucfirst($1))->new({
%$data,
intercom => $self,
});
} elsif ($data->{type} eq 'error.list') {
WebService::Intercom::Exception->throw(
request_id => $data->{request_id},
message => $data->{errors}->[0]->{message},
code => $data->{errors}->[0]->{code}
);
} else {
WebService::Intercom::Exception->throw({ message => "Unknown object type returned: $data->{type}"});
}
}
return;
} else {
# Failed request but we still got some json content
if ($response->header('Content-Type') =~ /json/) {
my $data;
eval {
$data = JSON::XS::decode_json($response->content());
};
if ($@) {
WebService::Intercom::Exception->throw({ message => "Failed to decode JSON result for request " . $request->as_string() . "\nResult was: " . $response->as_string()});
}
WebService::Intercom::Exception->throw(
request_id => $data->{request_id},
message => $data->{errors}->[0]->{message},
code => $data->{errors}->[0]->{code}
);
}
}
WebService::Intercom::Exception->throw({ message => "Got a bad response from request:\n" . $request->as_string() . "\nResult was: " . $response->as_string()});
}
}
=head1 SEE ALSO
See L<Moops> and L<Kavorka> to understand parameter signatures.
Also of course Intercom at L<http://www.intercom.io>.
=head1 AUTHOR
Rusty Conover <rusty+cpan@luckydinosaur.com>
=head1 COPYRIGHT
This software is copyright (c) 2015 by Lucky Dinosaur LLC. L<http://www.luckydinosaur.com>
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
=head1 DISCLAIMER OF WARRANTIES
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
=cut
1;