---
title: "Import message types from another package"
description: "Find a published package with axiom search and axiom info, pull its message types into your package with axiom import, and use them as node inputs and outputs."
category: guide
surfaces: [cli, sdk]
related: [concepts/type-system, concepts/nodes-packages-flows, getting-started/first-node, getting-started/first-flow, reference/axiom-yaml, reference/cli/axiom-import, reference/cli/axiom-search, reference/cli/axiom-info]
last_reviewed: 2026-06-06
---

# Import message types from another package

Every node input and output is a message — a Protocol Buffers type. Messages
do not have to be defined in your own package: `axiom import` downloads the
`.proto` definitions from any published package so your nodes can consume or
produce its types. This is how packages — even in different languages — share
a contract: a flow can wire another package's node output straight into your
node's input because both sides reference the same message type. See
[the type system](/docs/concepts/type-system) for how edges type-check.

## Prerequisites

- An Axiom package directory (one containing `axiom.yaml`) — create one with
  `axiom init`, see [write your first node](/docs/getting-started/first-node).
- Logged in via `axiom login`. `axiom import` requires it; `axiom search` and
  `axiom info` work without authentication.
- For Go, Python, and TypeScript packages, `axiom import` finishes by running
  `axiom generate`, which uses the same proto tooling as the rest of the
  local loop (`protoc` + `protoc-gen-go` for Go; `grpcio-tools` or `protoc`
  for Python; `protoc` + `protoc-gen-js` + `protoc-gen-ts` for TypeScript).
  Rust, Java, and C# packages need no extra tooling — their protos compile
  during the language build.

## The fast path

```bash
# Run inside your package directory (where axiom.yaml is).
axiom search --type messages TokensResult   # which package defines the type?
axiom info axiom-official/axiom-text-ops    # inspect its nodes and messages
axiom import axiom-official/axiom-text-ops  # download protos, update axiom.yaml, generate
axiom create node Analyze --input TokensResult --output AnalysisReport
```

After `axiom import`, the imported message types appear in
`axiom create node`'s message list and work as `--input`/`--output` exactly
like messages defined in your own `messages/messages.proto`.

## Search the marketplace

`axiom search` queries the marketplace; no login is needed.

```bash
axiom search                                # list recently published packages
axiom search text-ops                       # packages matching "text-ops"
axiom search --type nodes "tokenize"        # search nodes instead
axiom search --type messages TokensResult   # search messages instead
```

`--type` (shorthand `-t`) selects what to search: `packages` (the default),
`nodes`, or `messages`. Package results list name, version, language, node
count, and author. Node and message results include the package and version
that publish them — so `--type messages` answers "which package do I import
to get this type?"

## Inspect a package before importing

`axiom info` shows the full details of a published package; no login is
needed.

```bash
axiom info axiom-official/axiom-text-ops        # most recently published version
axiom info axiom-official/axiom-text-ops@0.1.0  # a specific version
```

When `@version` is omitted, the most recently published version is shown.
The output includes the description, author, license, and deploy status; the
package's live endpoint URL; links to its `openapi.json` and interactive API
docs (see [use the interactive API docs](/docs/guides/use-interactive-api-docs));
every node with its input → output message types; and every message the
package defines — the names you can use after importing.

A package can be *proto-only*: no nodes, just message types published for
other packages to import. See
[nodes, packages, and flows](/docs/concepts/nodes-packages-flows).

## Import the message types

```bash
# Run inside your package directory (where axiom.yaml is).
axiom import axiom-official/axiom-text-ops        # latest version
axiom import axiom-official/axiom-text-ops@0.1.0  # pinned version
```

`axiom import` requires a prior `axiom login` and must run inside a package
directory. It does four things:

1. Resolves the version (the most recently published one when `@version` is
   omitted) and downloads the package's `.proto` files.
2. Extracts them to `imports/<package>/<version>/`. A scoped name's `/`
   becomes `-` in the directory name, so `axiom-official/axiom-text-ops@0.1.0`
   lands in `imports/axiom-official-axiom-text-ops/0.1.0/`.
3. Records the dependency in `axiom.yaml` under `imports:` with the package,
   version, and imported message names.
4. Runs `axiom generate` so the language bindings appear under `gen/`
   immediately.

If an imported message has the same name as one in your local `messages/`,
the import fails with a collision error before writing anything — rename one
of the two first.

Commit `imports/` and the updated `axiom.yaml` to your repository:
`axiom push` builds your package from your git remote's `HEAD` commit, not
your working tree, so unpushed import files would be missing from the build.

## What axiom import writes

After importing, `axiom.yaml` carries the pinned dependency:

```yaml
# axiom.yaml (excerpt written by axiom import)
imports:
    - package: axiom-official/axiom-text-ops
      version: 0.1.0
      messages:
        - TextRequest
        - TokensResult
```

and the raw proto definitions live in your repository. The registry names
each downloaded file after the first message it defines (snake_case), not
after the upstream file name — here the package's `TextRequest` and
`TokensResult` share one proto file, downloaded as `text_request.proto`:

```text
imports/
└── axiom-official-axiom-text-ops/
    └── 0.1.0/
        └── text_request.proto
```

Each entry pins an exact version. Re-running the same import is safe — it
merges any newly published message names into the existing entry. Importing
a different version of the same package adds a second entry and a second
directory side by side; nothing is upgraded implicitly.

### Where the generated bindings go

- **Go** — `gen/imports/<package>/<version>/` (Go protobuf bindings).
- **Python** — one module per imported package, named after it:
  `gen/axiom_official_axiom_text_ops_messages_pb2.py` for the example above.
- **TypeScript** — `gen/imports/<package>/<version>/`, one `<file>_pb.js`
  plus a `<file>_pb.d.ts` type surface per downloaded proto file:
  `text_request_pb.js` and `text_request_pb.d.ts` for the example above.
- **Rust, Java, C#** — nothing at import time; the protos compile during the
  language build (`build.rs`/tonic-build for Rust, the protobuf-maven-plugin
  for Java, Grpc.Tools for C#).

## Use an imported message in a node signature

Reference imported messages by their simple name, exactly like local ones.
`axiom create node` lists them in its interactive message picker alongside
local messages, accepts them for `--input`/`--output`, and prints a note
about their origin:

```bash
# Run inside your package directory, after axiom import.
axiom create node Analyze --input TokensResult --output AnalysisReport
# Note: TokensResult is from imported package "axiom-official-axiom-text-ops@0.1.0"
```

The node's entry in `axiom.yaml` uses the simple name (`input: TokensResult`);
Axiom resolves which package it comes from via the `imports:` section. For
Go, Python, Java, C#, and Rust, the scaffolded node file already contains the
correct import statement. For TypeScript, the scaffold always imports from
`../gen/messages_pb` (the local messages module) — edit that line to point at
the imported module path shown below.

### A complete Go example

A Go node whose input type comes from the imported package and whose output
type is local. Replace `axiom-analytics` with the module path from your
package's `go.mod`:

```go
// nodes/analyze.go — input imported from axiom-official/axiom-text-ops
package nodes

import (
	"context"
	"fmt"
	"strings"

	"axiom-analytics/axiom"
	gen "axiom-analytics/gen"
	axiomtextops "axiom-analytics/gen/imports/axiom-official-axiom-text-ops/0.1.0"
)

// Analyze accepts tokenized text and produces a summary report with the
// token count and a human-readable listing of the tokens.
func Analyze(ctx context.Context, ax axiom.Context, input *axiomtextops.TokensResult) (*gen.AnalysisReport, error) {
	return &gen.AnalysisReport{
		Summary:   fmt.Sprintf("Processed %d tokens: %s", input.GetCount(), strings.Join(input.GetTokens(), ", ")),
		WordCount: input.GetCount(),
	}, nil
}
```

### Import statements per language

For a message `TokensResult` imported from
`axiom-official/axiom-text-ops@0.1.0`:

```python
# nodes/analyze.py — Python: per-package module under gen/
from gen.axiom_official_axiom_text_ops_messages_pb2 import TokensResult
```

```typescript
// nodes/analyze.ts — TypeScript: module under gen/imports/, named after the proto file
import { TokensResult } from '../gen/imports/axiom-official-axiom-text-ops/0.1.0/text_request_pb';
```

```rust
// nodes/analyze.rs — Rust: imported messages compile into the same module as local ones
use crate::gen::messages::TokensResult;
```

```java
// nodes/Analyze.java — Java: fully qualified type in the imports.* namespace
import imports.axiom_official.axiom_text_ops.TokensResult;
```

```csharp
// nodes/analyze.cs — C#: bring in the imports.* namespace, then use the simple name
using imports.axiom_official.axiom_text_ops;
```

Java and C# derive the namespace from the package name: `/` becomes `.` and
`-` becomes `_`, so `axiom-official/axiom-text-ops` becomes
`imports.axiom_official.axiom_text_ops`. Local messages stay where they
always are: `gen.Messages.<Name>` in Java, the `Gen` namespace in C#.

## Troubleshooting

- **"message X is listed in axiom.yaml imports but its proto definition is
  not downloaded"** — `axiom.yaml` declares the import but the files under
  `imports/` are missing (for example, a fresh checkout where `imports/` was
  never committed). Run the `axiom import <package>@<version>` command the
  error message prints.
- **"import collision: message X exists in both your local messages/ and the
  imported package"** — two messages with the same name cannot coexist.
  Rename your local message (or import a package that doesn't clash), then
  re-run the import.
- **"axiom.yaml not found — run this command from an Axiom package
  directory"** — `axiom import` only works inside a package directory; `cd`
  into the directory created by `axiom init` first.
- **"Not logged in"** — run `axiom login`. Search and info work logged out;
  import does not.

## Next steps

- [The type system](/docs/concepts/type-system) — how messages type-check
  across flow edges.
- [Push the package and build your first flow](/docs/getting-started/first-flow)
  — wire your node up in the canvas.
- [axiom.yaml reference](/docs/reference/axiom-yaml) — the full `imports:`
  schema.
- [axiom import](/docs/reference/cli/axiom-import),
  [axiom search](/docs/reference/cli/axiom-search), and
  [axiom info](/docs/reference/cli/axiom-info) — generated CLI reference for
  the three commands.
