Skip to main content
Some skills reach beyond the robot’s body — sending emails, calling APIs, retrieving information. These are ordinary code-defined skills that happen to talk to the network: they implement explicit protocols and handle authentication, errors, and connectivity.

What to keep in mind

Talking to an external service is a more deterministic domain than acting in the physical world, but it comes with its own constraints:
  • Protocol-based: follow defined APIs and standards
  • Atomic: many operations cannot be cancelled once started
  • Reliable: once working, behavior is consistent
  • Network-dependent: must handle connectivity issues

Built-in examples

SendEmail

Sends email notifications, typically for alerts or status updates.
Parameters:

SendPictureViaEmail

Sends an email with the robot’s current camera view attached.
This skill shows the value of a declared feed: image: MainImage makes the runtime wait for a real frame and fail the run up front if none arrives, so execute() never has to handle a missing picture.

RetrieveEmails

Fetches recent emails from the configured account.
Want to build your own version against your own account? There’s a complete implementation in the worked example below.

Building a service skill

Template

Read credentials inside execute(), not in an __init__: skill instances are built per run, and a constructor that raises takes the whole skill off the roster instead of failing one run with a message the agent can read.

Worked example: RetrieveEmails

A complete, runnable custom skill that fetches your latest Gmail messages over IMAP. Save it as ~/innate-os/workspace/custom_skills/retrieve_emails.py on the robot. The class name is the skill name, so this is local/retrieve_emails.

Best practices

Authentication
  • Store credentials in environment variables or a secret manager
  • Never hardcode passwords or API keys
  • Fail the run with a clear message when a credential is missing, so the agent can say so
Error handling Call self.fail() rather than returning an error — it raises, so there is no path where a failure silently reads as success:
An uncaught exception also fails the run with its message, so you only catch what you can say something useful about. Timeouts
  • Always set explicit timeouts on network calls
  • A blocking network call is not a cancel point — a Stop can’t interrupt it, so keep the timeout short enough that the robot stays responsive
Idempotency
  • Design operations to be safely retryable where possible
  • Consider partial failure scenarios

Reading robot state

A service skill often wants to send something the robot can see or know. Declare it the same way as any other feed:
Useful feeds for a service skill: See Robot state for the full list.

Cancellation

Many service operations are atomic and can’t be meaningfully interrupted. That’s fine — you don’t write anything for it. The framework latches the cancel, and the run reports CANCELLED once your call returns; a network request in flight simply finishes first. If a request is long enough that this matters, break the work into steps and call self.check_cancelled() between them so a Stop lands promptly.